Skip to content

Commit 1a9323b

Browse files
committed
Merge branch 'release/v2.0.0' into main
2 parents b4f6471 + db3556b commit 1a9323b

7 files changed

Lines changed: 24 additions & 160 deletions

File tree

analysis_options.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,6 @@ linter:
3535

3636
# **Error Prevention**
3737

38-
# Enforce non-nullable types where possible.
39-
always_require_non_null_named_parameters: true
40-
4138
# **Documentation**
4239
# Require documentation for public members.
4340
public_member_api_docs: false

packages/bounded/CHANGELOG.md

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

88
## [Unreleased]
99

10+
## [2.0.0] - 2026-02-16
11+
12+
### Removed
13+
14+
- Event collection from aggregate roots (no longer stores events internally)
15+
16+
## [1.0.0]
17+
1018
### Added
1119

1220
- Strongly-typed identity primitives (`Identity` interface, `TypedIdentity` class)
1321
- Value object support with structural equality (`ValueObject` mixin)
1422
- Entity mixin with identity-based equality
15-
- Aggregate root mixin with domain event collection
23+
- Aggregate root base class for domain model entry points
1624
- Domain event interface (`DomainEvent`)
17-
- Event collection methods on aggregates (record, retrieve, clear)
1825
- Pure Dart package with no infrastructure dependencies
Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import 'domain_event.dart';
21
import 'entity.dart';
32
import 'identity.dart';
43

@@ -9,12 +8,6 @@ import 'identity.dart';
98
/// aggregate root is responsible for ensuring all invariants within the
109
/// aggregate are maintained.
1110
///
12-
/// Aggregate roots can record domain events during state transitions.
13-
/// These events represent facts that have occurred within the domain
14-
/// and can be published or persisted by application layer code.
15-
///
16-
/// An aggregate root is an [Entity] with event collection capabilities.
17-
///
1811
/// Example:
1912
/// ```dart
2013
/// class Order extends AggregateRoot<OrderId> {
@@ -28,7 +21,6 @@ import 'identity.dart';
2821
/// throw StateError('Order can only be placed when pending');
2922
/// }
3023
/// status = OrderStatus.placed;
31-
/// recordEvent(OrderPlaced(id, DateTime.now()));
3224
/// }
3325
/// }
3426
/// ```
@@ -40,45 +32,4 @@ abstract class AggregateRoot<ID extends Identity> with Entity<ID> {
4032

4133
@override
4234
ID get id => _id;
43-
44-
final List<DomainEvent> _events = [];
45-
46-
/// Records a domain event that occurred during a state transition.
47-
///
48-
/// Events are stored internally and can be retrieved via [events]
49-
/// for publishing or persistence by application layer code.
50-
void recordEvent(DomainEvent event) {
51-
_events.add(event);
52-
}
53-
54-
/// Returns a read-only view of domain events recorded by this aggregate.
55-
///
56-
/// This list contains events that have been recorded but not yet cleared.
57-
List<DomainEvent> get events => List.unmodifiable(_events);
58-
59-
/// Clears all recorded domain events.
60-
///
61-
/// This should be called after events have been published or persisted
62-
/// to prevent them from being processed multiple times.
63-
void clearEvents() {
64-
_events.clear();
65-
}
66-
67-
/// Returns all recorded domain events and clears them in a single operation.
68-
///
69-
/// This is a convenience for the common application-layer flow:
70-
/// 1) perform a domain operation
71-
/// 2) publish/persist the resulting events
72-
/// 3) clear the pending event buffer
73-
///
74-
/// Events are returned in the order they were recorded.
75-
List<DomainEvent> pullEvents() {
76-
if (_events.isEmpty) {
77-
return const <DomainEvent>[];
78-
}
79-
80-
final drained = List<DomainEvent>.unmodifiable(_events);
81-
_events.clear();
82-
return drained;
83-
}
8435
}

packages/bounded/pubspec.yaml

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: bounded
22
description: "A minimal, framework-agnostic common kernel for Domain-Driven Design (DDD)."
3-
version: 1.0.0
3+
version: 2.0.0
44
homepage:
55

66
environment:
@@ -12,39 +12,4 @@ dev_dependencies:
1212
test: ^1.25.0
1313
lints: ^6.0.0
1414

15-
# For information on the generic Dart part of this file, see the
16-
# following page: https://dart.dev/tools/pub/pubspec
17-
18-
# The following section is specific to Flutter packages.
1915
flutter:
20-
21-
# To add assets to your package, add an assets section, like this:
22-
# assets:
23-
# - images/a_dot_burr.jpeg
24-
# - images/a_dot_ham.jpeg
25-
#
26-
# For details regarding assets in packages, see
27-
# https://flutter.dev/to/asset-from-package
28-
#
29-
# An image asset can refer to one or more resolution-specific "variants", see
30-
# https://flutter.dev/to/resolution-aware-images
31-
32-
# To add custom fonts to your package, add a fonts section here,
33-
# in this "flutter" section. Each entry in this list should have a
34-
# "family" key with the font family name, and a "fonts" key with a
35-
# list giving the asset and other descriptors for the font. For
36-
# example:
37-
# fonts:
38-
# - family: Schyler
39-
# fonts:
40-
# - asset: fonts/Schyler-Regular.ttf
41-
# - asset: fonts/Schyler-Italic.ttf
42-
# style: italic
43-
# - family: Trajan Pro
44-
# fonts:
45-
# - asset: fonts/TrajanPro.ttf
46-
# - asset: fonts/TrajanPro_Bold.ttf
47-
# weight: 700
48-
#
49-
# For details regarding fonts in packages, see
50-
# https://flutter.dev/to/font-from-package

packages/bounded/test/aggregate_test.dart

Lines changed: 5 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -37,93 +37,37 @@ class Order extends AggregateRoot<OrderId> {
3737
throw StateError('Order can only be placed when pending');
3838
}
3939
status = OrderStatus.placed;
40-
recordEvent(OrderPlaced(id, at));
4140
}
4241

4342
void ship() {
4443
if (status != OrderStatus.placed) {
4544
throw StateError('Order can only be shipped when placed');
4645
}
4746
status = OrderStatus.shipped;
48-
recordEvent(OrderShipped(id));
4947
}
5048
}
5149

5250
void main() {
5351
group('AggregateRoot', () {
54-
test('aggregate can record domain events during transitions', () {
52+
test('aggregate can perform state transitions', () {
5553
final order = Order(const OrderId('order-123'));
5654
final placedAt = DateTime(2026, 1, 21);
5755

5856
order.place(placedAt);
5957

60-
expect(order.events, hasLength(1));
61-
expect(order.events.first, isA<OrderPlaced>());
62-
final event = order.events.first as OrderPlaced;
63-
expect(event.orderId, equals(order.id));
64-
expect(event.placedAt, equals(placedAt));
58+
expect(order.status, equals(OrderStatus.placed));
6559
});
6660

67-
test('aggregate records multiple events', () {
61+
test('aggregate can perform multiple transitions', () {
6862
final order = Order(const OrderId('order-123'));
6963

7064
order.place(DateTime.now());
7165
order.ship();
7266

73-
expect(order.events, hasLength(2));
74-
expect(order.events[0], isA<OrderPlaced>());
75-
expect(order.events[1], isA<OrderShipped>());
67+
expect(order.status, equals(OrderStatus.shipped));
7668
});
7769

78-
test('events property returns read-only view', () {
79-
final order = Order(const OrderId('order-123'));
80-
order.place(DateTime.now());
81-
82-
final events = order.events;
83-
expect(() => (events as List).add(OrderShipped(order.id)), throwsUnsupportedError);
84-
});
85-
86-
test('clearEvents removes all recorded events', () {
87-
final order = Order(const OrderId('order-123'));
88-
order.place(DateTime.now());
89-
90-
expect(order.events, hasLength(1));
91-
92-
order.clearEvents();
93-
94-
expect(order.events, isEmpty);
95-
});
96-
97-
test('pullEvents drains events in order and clears', () {
98-
final order = Order(const OrderId('order-123'));
99-
100-
order.place(DateTime(2026, 1, 21));
101-
order.ship();
102-
103-
final drained = order.pullEvents();
104-
105-
expect(drained, hasLength(2));
106-
expect(drained[0], isA<OrderPlaced>());
107-
expect(drained[1], isA<OrderShipped>());
108-
expect(order.events, isEmpty);
109-
});
110-
111-
test('pullEvents returns empty list when no events are recorded', () {
112-
final order = Order(const OrderId('order-123'));
113-
114-
final drained = order.pullEvents();
115-
116-
expect(drained, isEmpty);
117-
expect(order.events, isEmpty);
118-
});
119-
120-
test('aggregate starts with no events', () {
121-
final order = Order(const OrderId('order-123'));
122-
123-
expect(order.events, isEmpty);
124-
});
125-
126-
test('aggregate enforces invariants without infrastructure', () {
70+
test('aggregate enforces invariants', () {
12771
final order = Order(const OrderId('order-123'));
12872

12973
// Cannot ship without placing first

packages/bounded_lints/example/pubspec.lock

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -195,18 +195,18 @@ packages:
195195
dependency: transitive
196196
description:
197197
name: json_annotation
198-
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
198+
sha256: "805fa86df56383000f640384b282ce0cb8431f1a7a2396de92fb66186d8c57df"
199199
url: "https://pub.dev"
200200
source: hosted
201-
version: "4.9.0"
201+
version: "4.10.0"
202202
lints:
203203
dependency: "direct dev"
204204
description:
205205
name: lints
206-
sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0
206+
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
207207
url: "https://pub.dev"
208208
source: hosted
209-
version: "6.0.0"
209+
version: "6.1.0"
210210
logging:
211211
dependency: transitive
212212
description:
@@ -227,10 +227,10 @@ packages:
227227
dependency: transitive
228228
description:
229229
name: meta
230-
sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
230+
sha256: "9f29b9bcc8ee287b1a31e0d01be0eae99a930dbffdaecf04b3f3d82a969f296f"
231231
url: "https://pub.dev"
232232
source: hosted
233-
version: "1.18.0"
233+
version: "1.18.1"
234234
package_config:
235235
dependency: transitive
236236
description:
@@ -275,10 +275,10 @@ packages:
275275
dependency: transitive
276276
description:
277277
name: source_span
278-
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
278+
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
279279
url: "https://pub.dev"
280280
source: hosted
281-
version: "1.10.1"
281+
version: "1.10.2"
282282
stack_trace:
283283
dependency: transitive
284284
description:

packages/bounded_lints/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: bounded_lints
22
description: Custom lints for Domain-Driven Design principles aligned with bounded.
3-
version: 1.0.0
3+
version: 2.0.0
44
homepage:
55

66
environment:

0 commit comments

Comments
 (0)