-
Notifications
You must be signed in to change notification settings - Fork 16
/
AggregateRoot.java
86 lines (71 loc) · 2.77 KB
/
AggregateRoot.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// Copyright © 2016-2019 Esko Luontola
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0
package fi.luontola.cqrshotel.framework;
import fi.luontola.cqrshotel.framework.util.EventListener;
import fi.luontola.cqrshotel.framework.util.EventListeners;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import static fi.luontola.cqrshotel.framework.util.EventListeners.Requirements.MUST_BE_PRIVATE;
/**
* Aggregate root is the consistency boundary for doing atomic transactions.
* You may obtain {@code AggregateRoot} instances from a {@link Repository}.
* <p>
* The methods of an {@code AggregateRoot} implementation MUST follow a strong dichotomy of
* event listeners and command handlers:
* <p>
* <b>Event listeners</b> are private methods, annotated with {@link EventListener},
* which take an {@link Event} as parameter. Their purpose is to read data from previously published events.
* The framework will call them automatically for all old and newly published events of this aggregate.
* <em>Event listeners MAY mutate state and MUST NOT throw exceptions.</em>
* <p>
* <b>Command handlers</b> are public methods which take arbitrary parameters and are called by users.
* Their purpose is to validate business rules and publish new events using {@link #publish(Event)}.
* The published events will be persisted atomically by {@link Repository#save}.
* <em>Command handlers MUST NOT mutate state and MAY throw exceptions.</em>
*/
public abstract class AggregateRoot {
private final EventListeners eventListeners;
private final List<Event> changes = new ArrayList<>();
private UUID id;
private int version = 0;
public AggregateRoot() {
eventListeners = EventListeners.of(this, MUST_BE_PRIVATE);
}
public final UUID getId() {
if (id == null) {
throw new IllegalStateException("id not set");
}
return id;
}
final void setId(UUID id) {
if (this.id != null) {
throw new IllegalStateException("id already set");
}
this.id = id;
}
public final int getVersion() {
return version;
}
public final List<Event> getUncommittedChanges() {
return Collections.unmodifiableList(changes);
}
public final void markChangesAsCommitted() {
changes.clear();
}
public final void loadFromHistory(Iterable<Event> history) {
for (var event : history) {
applyChange(event);
}
}
protected final void publish(Event event) {
applyChange(event);
changes.add(event);
}
private void applyChange(Event event) {
eventListeners.send(event);
version++;
}
}