You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* Apply fixes from PHP CS Fixer and PHPStan. ([4a9ba6b](https://github.com/geekcell/ddd-symfony-bundle/commit/4a9ba6b6f06d3ab256038551413d93e9b295d1ad))
This Symfony bundle augments [geekcell/php-ddd](https://github.com/geekcell/php-ddd) with framework-specific implementations to enable seamless [domain driven design](https://martinfowler.com/tags/domain%20driven%20design.html) in a familiar environment.
@@ -10,33 +21,91 @@ To use this bundle, require it in Composer
10
21
composer require geekcell/ddd-bundle
11
22
```
12
23
13
-
## Quickstart
24
+
## Generator Commands
25
+
26
+
This bundle adds several [MakerBundle](https://symfony.com/bundles/SymfonyMakerBundle/current/index.html) commands to generate commonly used components.
27
+
28
+
In order to use them in your Symfony project, you need to require it with composer first
29
+
30
+
```bash
31
+
composer require symfony/maker-bundle
32
+
```
33
+
34
+
### Available Commands
14
35
15
-
-[Aggregate Root](#aggregate-root)
16
-
-[Repositories](#repositories)
17
-
-[Command & Query Bus](#command--query-bus)
18
-
-[Supporting Tools](#supporting-tools)
36
+
```bash
37
+
make:ddd:command Creates a new command class and handler
38
+
make:ddd:controller Creates a new controller class
39
+
make:ddd:model Creates a new domain model class
40
+
make:ddd:query Creates a new query class and handler
41
+
make:ddd:resource Creates a new API Platform resource
42
+
```
43
+
44
+
## Building Blocks
45
+
46
+
### Model & Repository
19
47
20
-
## Aggregate Root
48
+
The **domain model** is a representation of the domain concepts and business logic within your project. The **repository** on the other hand is an abstraction layer that provides a way to access and manipulate domain objects without exposing the details of the underlying data persistence mechanism (such as a database or file system).
21
49
22
-
Extend from `AggregateRoot` to record and commit domain events. Domain events must implement the (marker) interface `DomainEvent`. Events will be dispatched via the currently configured Symfony event dispatcher.
50
+
Since Doctrine is the de-facto persistence layer for Symfony, this bundle also provides an (opinionated) implementation for a Doctrine-based repository.
23
51
24
-
### Example Usage
52
+
#### Generator Command(s)
53
+
54
+
This command can be used to generate:
55
+
56
+
- The domain model class.
57
+
- A repository class for the model.
58
+
- The model's identity class as value object (optional).
59
+
- A Doctrine database entity configuration, either as annotation or separate config file (optional).
60
+
- A custom Doctrine type for the model's identity class (optional).
61
+
62
+
```bash
63
+
Description:
64
+
Creates a new domain model class
65
+
66
+
Usage:
67
+
make:ddd:model [options] [--] [<name>]
68
+
69
+
Arguments:
70
+
name The name of the model class (e.g. Customer)
71
+
72
+
Options:
73
+
--aggregate-root Marks the model as aggregate root
74
+
--entity=ENTITY Use this model as Doctrine entity
75
+
--with-identity=WITH-IDENTITY Whether an identity value object should be created
76
+
--with-suffix Adds the suffix "Model" to the model class name
77
+
```
78
+
79
+
### AggregateRoot & Domain Events
80
+
81
+
Optionally, by inheriting from `AggregateRoot`, you can make a model class an **aggregate root**, which is used to encapsulate a group of related objects, along with the behavior and rules that apply to them. The aggregate root is usually responsible for managing the lifecycle of the objects within the aggregate, and for coordinating any interactions between them.
82
+
83
+
The `AggregateRoot` base class comes with some useful functionality to record and dispatch **domain events**, which represent significant occurrences or state changes within the domain of a software system.
84
+
85
+
#### Generator Command(s)
86
+
87
+
N/A
88
+
89
+
#### Example Usage
25
90
26
91
```php
92
+
93
+
// src/Domain/Event/OrderPlacedEvent.php
94
+
27
95
use GeekCell\Ddd\Contracts\Domain\Event as DomainEvent;
28
-
use GeekCell\DddBundle\Domain\AggregateRoot;
29
96
30
-
class OrderPlacedEvent implements DomainEvent
97
+
readonly class OrderPlacedEvent implements DomainEvent
31
98
{
32
99
public function __construct(
33
-
private readonly Order $order,
100
+
public Order $order,
34
101
) {
35
102
}
36
-
37
-
// Getters etc.
38
103
}
39
104
105
+
// src/Domain/Model/Order.php
106
+
107
+
use GeekCell\DddBundle\Domain\AggregateRoot;
108
+
40
109
class Order extends AggregateRoot
41
110
{
42
111
public function save(): void
@@ -47,6 +116,8 @@ class Order extends AggregateRoot
47
116
// ...
48
117
}
49
118
119
+
// Actual usage ...
120
+
50
121
$order = new Order( /* ... */ );
51
122
$order->save();
52
123
$order->commit(); // All recorded events will be dispatched and released
@@ -56,33 +127,51 @@ _Hint: If you want to dispatch an event directly, use `AggregateRoot::dispatch()
56
127
57
128
If you cannot (or don't want to) extend from `AggregateRoot`, you can alternative use `DispatchableTrait` to add dispatching capabilities to any class. The former is however the recommended way.
58
129
59
-
##Repositories
130
+
### Command & Query
60
131
61
-
_coming soon..._
132
+
You can use `CommandBus` and `QueryBus` as services to implement [CQRS](https://martinfowler.com/bliki/CQRS.html). Internally, both buses will use the [Symfony messenger](https://symfony.com/doc/current/messenger.html) to dispatch commands and queries.
62
133
63
-
##Command & Query Bus
134
+
#### Generator Command(s)
64
135
65
-
You can use `CommandBus` and `QueryBus` as services to implement [CQRS](https://martinfowler.com/bliki/CQRS.html). Internally, both buses will use the [Symfony messenger](https://symfony.com/doc/current/messenger.html) as "backend".
136
+
These commands can be used to generate:
137
+
138
+
- A command and command handler class.
139
+
- A query and query handler class.
140
+
141
+
The query / command generated is just an empty class. The handler class is registered as a message handler for the configured [Symfony Messenger](https://symfony.com/doc/current/messenger.html).
142
+
143
+
```bash
144
+
Description:
145
+
Creates a new query|command class and handler
66
146
67
-
## Example Usage
147
+
Usage:
148
+
make:ddd:query|command [<name>]
149
+
150
+
Arguments:
151
+
name The name of the query|command class (e.g. Customer)
152
+
```
153
+
154
+
#### Example Usage
68
155
69
156
```php
70
157
// src/Application/Query/TopRatedBookQuery.php
158
+
71
159
use GeekCell\Ddd\Contracts\Application\Query;
72
160
73
-
class TopRatedBooksQuery implements Query
161
+
readonly class TopRatedBooksQuery implements Query
@@ -124,32 +214,47 @@ class BookController extends AbstractController
124
214
}
125
215
```
126
216
127
-
##Supporting Tools
217
+
### Controller
128
218
129
-
### Facades
219
+
A standard Symfony controller, but augmented with command and query bus(es).
130
220
131
-
Facades are heavily inspired by [Laravel's Facades](https://laravel.com/docs/facades) and are more or less singletons on steroids. They are basically a shortcut to services inside the DIC.
221
+
#### Generator Command
132
222
133
-
```php
134
-
use GeekCell\DddBundle\Support\Facades\EventDispatcher;
223
+
This command can be used to generate a controller with optional `QueryBus` and `CommandBus` dependencies.
135
224
136
-
EventDispatcher::dispatch($someEvent);
225
+
```bash
226
+
Description:
227
+
Creates a new controller class
228
+
229
+
Usage:
230
+
make:ddd:controller [options] [--] [<name>]
137
231
138
-
// same as: $container->get('event_dispatcher')->dispatch($someEvent);
232
+
Arguments:
233
+
name The name of the model class (e.g. Customer)
234
+
235
+
Options:
236
+
--include-query-bus Add a query bus dependency
237
+
--include-command-bus Add a command bus dependency
139
238
```
140
239
141
-
You can create your own facades by extending from `Facade` and implementing the `Facade::getFacadeAccessor()` method to return the DIC service alias.
240
+
### Resource
142
241
143
-
```php
144
-
use GeekCell\DddBundle\Support\Facades\Facade;
242
+
An [API Platform](https://api-platform.com/) resource, but instead of using the standard approach of using a combined entity/resource approach, it is preferred to separate model (domain layer) and API Platform specific resource (infrastructure layer)
145
243
146
-
class Logger extends Facade
147
-
{
148
-
protected static function getFacadeAccessor(): string
149
-
{
150
-
return 'logger';
151
-
}
152
-
}
153
-
```
244
+
#### Generator Command
245
+
246
+
Minimum required API Platform version is [2.7](https://api-platform.com/docs/core/upgrade-guide/#api-platform-2730) for the [new metadata system](https://api-platform.com/docs/core/upgrade-guide/#apiresource-metadata).
247
+
248
+
```bash
249
+
Description:
250
+
Creates a new API Platform resource
154
251
155
-
Although facades are better testable than regular singletons, it is highly recommended to only use them sparringly and always prefer normal dependency injection when possible.
252
+
Usage:
253
+
make:ddd:resource [options] [--] [<name>]
254
+
255
+
Arguments:
256
+
name The name of the model class to create the resource for (e.g. Customer). Model must exist already.
0 commit comments