Skip to content

Commit 7d7f1f3

Browse files
authored
Merge pull request #18 from zooper-lib/feature/aggregate-renames
Code refactors to get rid if "Aggregate" naming
2 parents 5048624 + 35a0e84 commit 7d7f1f3

102 files changed

Lines changed: 958 additions & 474 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Added `@OperationTarget()` and `@OperationFor(...)` annotations in `continuum` to support operation-driven mutation without requiring `AggregateRoot`.
13+
14+
### Changed
15+
16+
- `continuum_generator` now discovers operation targets via `@OperationTarget()` (and still supports `AggregateRoot` as a legacy marker).
17+
- `continuum_lints` now applies missing-handler and missing-creation-factory checks to `@OperationTarget()` classes.
18+
- `EventSourcingStore` and `StateBasedStore` now accept `targets:` instead of `aggregates:` (with `aggregates:` kept as a deprecated alias).
19+
- State-based persistence now prefers `TargetPersistenceAdapter<T>`; `AggregatePersistenceAdapter<T>` remains as a deprecated alias for backward compatibility.
20+
21+
### Fixed
22+
23+
- Example `abstract_interface_targets.dart` is now discoverable by codegen by marking its target types with `@OperationTarget()`.
24+
25+
### Deprecated
26+
27+
- Deprecated `@AggregateEvent(...)` in favor of operation-oriented annotations.
28+
1029
## [5.1.0] - 2026-02-20
1130

1231
### Added

README.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ Continuum provides a comprehensive event sourcing framework for Dart application
99
| Layer | Package | Purpose |
1010
|-------|---------|---------|
1111
| 0 | **`continuum`** | Core types: annotations, events, identity, dispatch registries |
12-
| 0 | **`continuum_generator`** | Code generator for aggregate and event boilerplate |
12+
| 0 | **`continuum_generator`** | Code generator for operation and event boilerplate |
1313
| 1 | **`continuum_uow`** | Unit of Work session engine: sessions, transactional runner, commit handler |
1414
| 2 | **`continuum_event_sourcing`** | Event sourcing persistence: event stores, serialization, projections |
15-
| 2 | **`continuum_state`** | State-based persistence: REST/DB adapter-driven aggregate persistence |
15+
| 2 | **`continuum_state`** | State-based persistence: REST/DB adapter-driven target persistence |
1616
| 3 | **`continuum_store_memory`** | In-memory EventStore for testing |
1717
| 3 | **`continuum_store_hive`** | Hive-backed EventStore for local persistence |
1818
| 3 | **`continuum_store_sembast`** | Sembast-backed EventStore for cross-platform persistence |
@@ -70,10 +70,12 @@ import 'package:continuum/continuum.dart';
7070

7171
part 'shopping_cart.g.dart';
7272

73-
class ShoppingCart extends AggregateRoot<String> with _$ShoppingCartEventHandlers {
73+
@OperationTarget()
74+
class ShoppingCart with _$ShoppingCartEventHandlers {
75+
String id;
7476
List<String> items;
7577

76-
ShoppingCart._({required super.id, required this.items});
78+
ShoppingCart._({required this.id, required this.items});
7779
static ShoppingCart createFromCartCreated(CartCreated event) {
7880
return ShoppingCart._(id: event.cartId, items: []);
7981
}
@@ -84,7 +86,7 @@ class ShoppingCart extends AggregateRoot<String> with _$ShoppingCartEventHandler
8486
}
8587
}
8688

87-
@AggregateEvent(of: ShoppingCart, type: 'cart.created', creation: true)
89+
@OperationFor(type: ShoppingCart, key: 'cart.created', creation: true)
8890
class CartCreated implements ContinuumEvent {
8991
final String cartId;
9092

@@ -121,7 +123,7 @@ import 'continuum.g.dart';
121123
122124
final store = EventSourcingStore(
123125
eventStore: InMemoryEventStore(),
124-
aggregates: $aggregateList,
126+
targets: $aggregateList,
125127
);
126128
127129
final session = store.openSession();
@@ -144,7 +146,7 @@ import 'continuum.g.dart';
144146
145147
final store = StateBasedStore(
146148
adapters: {ShoppingCart: CartApiAdapter(httpClient)},
147-
aggregates: $aggregateList,
149+
targets: $aggregateList,
148150
);
149151
150152
final session = store.openSession();
@@ -159,7 +161,7 @@ await session.saveChangesAsync(); // Adapter persists to backend
159161

160162
### continuum
161163

162-
Core library providing annotations (`@Aggregate`, `@AggregateEvent`, `@Projection`), event contracts (`ContinuumEvent`), identity types (`EventId`, `StreamId`), dispatch registries, `EventApplicationMode`, and core exceptions.
164+
Core library providing annotations (`@OperationTarget`, `@OperationFor`, `@Projection`), event contracts (`ContinuumEvent`), identity types (`EventId`, `StreamId`), dispatch registries, `EventApplicationMode`, and core exceptions.
163165

164166
### continuum_generator
165167

@@ -191,7 +193,7 @@ Sembast-backed `EventStore` implementation for cross-platform local persistence.
191193

192194
### continuum_lints
193195

194-
Custom lint rules for `@Aggregate` and `@Projection` classes.
196+
Custom lint rules for operation targets and projections.
195197

196198
## License
197199

doc/draft/extract-unit-of-work-package.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Layer 3 ─ Store implementations
4141

4242
| Layer | Package | Contains |
4343
|---|---|---|
44-
| 0 | `continuum` | `Operation`, `ContinuumEvent`, `@AggregateEvent`, `@Projection`, `AggregateFactoryRegistry`, `EventApplierRegistry`, `EventApplicationMode`, `StreamId`, `GeneratedAggregate`, codegen annotations, all operation/event base contracts |
44+
| 0 | `continuum` | `Operation`, `ContinuumEvent`, `@OperationTarget`, `@OperationFor`, `@Projection`, `AggregateFactoryRegistry`, `EventApplierRegistry`, `EventApplicationMode`, `StreamId`, `GeneratedAggregate`, codegen annotations, all operation/event base contracts |
4545
| 1 | `continuum_uow` | `Session`, `SessionBase`, `TrackedEntity`, `SessionStore`, `TransactionalRunner`, `CommitHandler`, `ConcurrencyException`, `InvalidOperationException`, `UnsupportedOperationException`, `PartialSaveException` |
4646
| 2a | `continuum_es` | `EventSourcingStore`, `SessionImpl`, `EventStore`, `AtomicEventStore`, `EventSerializer`, `EventSerializerRegistry`, `JsonEventSerializer`, `StoredEvent`, `ExpectedVersion`, `StreamAppendBatch`, `ProjectionEventStore`, all projection types |
4747
| 2b | `continuum_state` | `StateBasedStore`, `StateBasedSession`, `AggregatePersistenceAdapter`, `PermanentAdapterException`, `TransientAdapterException` |
@@ -454,7 +454,7 @@ These are Continuum core — the mutation framework primitives:
454454

455455
- `Operation` marker interface
456456
- `ContinuumEvent` (`implements Operation` + `BoundedDomainEvent` integration)
457-
- `@AggregateEvent`, `@Projection` annotations
457+
- `@OperationTarget`, `@OperationFor`, `@Projection` annotations
458458
- `AggregateFactoryRegistry`, `EventApplierRegistry`
459459
- `EventApplicationMode`
460460
- `StreamId`

doc/draft/session-store-architecture.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,14 +224,14 @@ This is a **store-level or session-level configuration**, not a per-event decisi
224224
// Eager (default) — events mutate the aggregate immediately
225225
final store = EventSourcingStore(
226226
eventStore: SembastEventStore(database),
227-
aggregates: [$User, $Playlist],
227+
targets: [$User, $Playlist],
228228
applicationMode: EventApplicationMode.eager,
229229
);
230230
231231
// Deferred — events are recorded but applied only on successful save
232232
final store = EventSourcingStore(
233233
eventStore: SembastEventStore(database),
234-
aggregates: [$User, $Playlist],
234+
targets: [$User, $Playlist],
235235
applicationMode: EventApplicationMode.deferred,
236236
);
237237
```
@@ -1064,7 +1064,7 @@ State-Based: applyAsync → save to Backend → commitHandler → proj
10641064
// main.dart
10651065
final store = EventSourcingStore(
10661066
eventStore: SembastEventStore(database),
1067-
aggregates: [$AudioFile, $Playlist],
1067+
targets: [$AudioFile, $Playlist],
10681068
);
10691069
10701070
final runner = TransactionalRunner(

packages/continuum/README.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ dev_dependencies:
1919
2020
## What This Package Provides
2121
22-
- **Annotations**: `@AggregateEvent()`, `@Projection()`
22+
- **Annotations**: `@OperationTarget()`, `@OperationFor(...)`, `@Projection()`
2323
- **Event contract**: `ContinuumEvent` interface
2424
- **Identity types**: `EventId`, `StreamId`
2525
- **Operation enum**: `Operation.create`, `Operation.mutate`
@@ -48,18 +48,20 @@ Continuum is organized into four layers:
4848

4949
## Quick Start
5050

51-
### Define Your Aggregate
51+
### Define Your Target
5252

5353
```dart
5454
import 'package:continuum/continuum.dart';
5555
5656
part 'user.g.dart';
5757
58-
class User extends AggregateRoot<String> with _$UserEventHandlers {
58+
@OperationTarget()
59+
class User with _$UserEventHandlers {
60+
String id;
5961
String name;
6062
String email;
6163
62-
User._({required super.id, required this.name, required this.email});
64+
User._({required this.id, required this.name, required this.email});
6365
6466
static User createFromUserRegistered(UserRegistered event) {
6567
return User._(id: event.userId, name: event.name, email: event.email);
@@ -77,7 +79,7 @@ class User extends AggregateRoot<String> with _$UserEventHandlers {
7779
```dart
7880
import 'package:continuum/continuum.dart';
7981
80-
@AggregateEvent(of: User, type: 'user.registered', creation: true)
82+
@OperationFor(type: User, key: 'user.registered', creation: true)
8183
class UserRegistered implements ContinuumEvent {
8284
UserRegistered({
8385
required this.userId,
@@ -111,7 +113,7 @@ dart run build_runner build
111113

112114
This creates:
113115
- `user.g.dart` with `_$UserEventHandlers` mixin
114-
- `lib/continuum.g.dart` with `$aggregateList`
116+
- `lib/continuum.g.dart` with `$aggregateList` (auto-discovered operation targets)
115117

116118
## Custom Lints (Recommended)
117119

packages/continuum/example/lib/abstract_interface_aggregates.dart renamed to packages/continuum/example/lib/abstract_interface_targets.dart

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
1-
/// Example: Abstract and Interface Aggregates
1+
/// Example: Abstract and Interface Targets
22
///
33
/// This example demonstrates that Continuum can generate event handlers and
4-
/// dispatch logic for aggregates declared as an `abstract class` or an
4+
/// dispatch logic for targets declared as an `abstract class` or an
55
/// `interface class`.
66
library;
77

88
import 'package:bounded/bounded.dart';
99
import 'package:continuum/continuum.dart';
1010

11-
part 'abstract_interface_aggregates.g.dart';
11+
part 'abstract_interface_targets.g.dart';
1212

1313
void main() {
1414
print('═══════════════════════════════════════════════════════════════════');
15-
print('Example: Abstract and Interface Aggregates');
15+
print('Example: Abstract and Interface Targets');
1616
print('═══════════════════════════════════════════════════════════════════');
1717
print('');
1818

19-
_runAbstractAggregateExample();
19+
_runAbstractTargetExample();
2020
print('');
21-
_runInterfaceAggregateExample();
21+
_runInterfaceTargetExample();
2222
}
2323

24-
void _runAbstractAggregateExample() {
25-
print('ABSTRACT AGGREGATE');
24+
void _runAbstractTargetExample() {
25+
print('ABSTRACT TARGET');
2626

2727
final user = AbstractUser(
2828
id: const AbstractUserId('abstract-user-1'),
@@ -42,8 +42,8 @@ void _runAbstractAggregateExample() {
4242
print(' ✓ Event dispatch works via AbstractUserBase');
4343
}
4444

45-
void _runInterfaceAggregateExample() {
46-
print('CONCRETE AGGREGATE');
45+
void _runInterfaceTargetExample() {
46+
print('CONCRETE TARGET');
4747

4848
final user = UserContract(
4949
id: const UserContractId('contract-user-1'),
@@ -66,23 +66,25 @@ final class AbstractUserId extends TypedIdentity<String> {
6666
const AbstractUserId(super.value);
6767
}
6868

69-
/// An abstract aggregate base type.
69+
/// An abstract target base type.
7070
///
7171
/// The generator produces:
7272
/// - `mixin _$AbstractUserBaseEventHandlers`
7373
/// - `extension $AbstractUserBaseEventDispatch on AbstractUserBase`
74-
abstract class AbstractUserBase extends AggregateRoot<AbstractUserId> with _$AbstractUserBaseEventHandlers {
74+
@OperationTarget()
75+
abstract class AbstractUserBase with _$AbstractUserBaseEventHandlers {
7576
AbstractUserBase({
76-
required AbstractUserId id,
77+
required this.id,
7778
required this.email,
7879
required this.name,
79-
}) : super(id);
80+
});
8081

82+
final AbstractUserId id;
8183
String email;
8284
final String name;
8385
}
8486

85-
/// A concrete implementation of the abstract aggregate.
87+
/// A concrete implementation of the abstract target.
8688
class AbstractUser extends AbstractUserBase {
8789
AbstractUser({
8890
required super.id,
@@ -100,12 +102,15 @@ final class UserContractId extends TypedIdentity<String> {
100102
const UserContractId(super.value);
101103
}
102104

103-
/// A concrete implementation of the interface aggregate.
104-
class UserContract extends AggregateRoot<UserContractId> with _$UserContractEventHandlers {
105+
/// A concrete implementation of the interface target.
106+
@OperationTarget()
107+
class UserContract with _$UserContractEventHandlers {
105108
UserContract({
106-
required UserContractId id,
109+
required this.id,
107110
required this.displayName,
108-
}) : super(id);
111+
});
112+
113+
final UserContractId id;
109114

110115
String displayName;
111116

@@ -116,7 +121,7 @@ class UserContract extends AggregateRoot<UserContractId> with _$UserContractEven
116121
}
117122

118123
/// Event that changes an abstract user's email.
119-
@AggregateEvent(of: AbstractUserBase, type: 'example.abstract_user.email_changed')
124+
@OperationFor(type: AbstractUserBase, key: 'example.abstract_user.email_changed')
120125
class AbstractUserEmailChanged implements ContinuumEvent {
121126
AbstractUserEmailChanged({
122127
required this.newEmail,
@@ -155,8 +160,8 @@ class AbstractUserEmailChanged implements ContinuumEvent {
155160
};
156161
}
157162

158-
/// Event that renames a user implementing an interface aggregate.
159-
@AggregateEvent(of: UserContract, type: 'example.contract_user.renamed')
163+
/// Event that renames a user implementing an interface target.
164+
@OperationFor(type: UserContract, key: 'example.contract_user.renamed')
160165
class ContractUserRenamed implements ContinuumEvent {
161166
ContractUserRenamed({
162167
required this.newDisplayName,

0 commit comments

Comments
 (0)