Skip to content

Commit 37f3e74

Browse files
authored
Merge branch 'geekcell:main' into main
2 parents 8d45c15 + ab1545b commit 37f3e74

Some content is hidden

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

56 files changed

+3526
-830
lines changed

.github/workflows/test.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Tests
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- main
8+
9+
concurrency:
10+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
unit-test:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v3
18+
- uses: php-actions/composer@v6
19+
- name: PHPUnit Tests
20+
uses: php-actions/phpunit@v3
21+
env:
22+
XDEBUG_MODE: coverage
23+
with:
24+
version: 9.6.5
25+
php_version: 8.2.3
26+
configuration: phpunit.xml
27+
php_extensions: "xdebug"
28+
coverage_text: true
29+
coverage_clover: reports/coverage.xml
30+
31+
# SonarCloud
32+
- name: SonarCloud Scan
33+
uses: SonarSource/sonarcloud-github-action@master
34+
env:
35+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
36+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,3 @@
33

44
/.php-cs-fixer.cache
55
/composer.phar
6-
/phpunit.xml

.php-cs-fixer.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
<?php
22

33
$finder = PhpCsFixer\Finder::create()
4-
->exclude(['var', 'vendor', 'migrations', 'fixtures'])
4+
->exclude([
5+
'src/Resources/skeleton',
6+
'vendor'
7+
])
58
->in(__DIR__)
69
;
710

CHANGELOG.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,52 @@
11
# Changelog
22

3+
## [1.3.0](https://github.com/geekcell/ddd-symfony-bundle/compare/v1.2.3...v1.3.0) (2023-04-27)
4+
5+
6+
### Features
7+
8+
* add `base-path` ([4673b6d](https://github.com/geekcell/ddd-symfony-bundle/commit/4673b6d515ec995828cbef9cde10e04c1aab7303))
9+
10+
## [1.2.3](https://github.com/geekcell/ddd-symfony-bundle/compare/v1.2.2...v1.2.3) (2023-03-31)
11+
12+
13+
### Miscellaneous Chores
14+
15+
* Update `geekcell/ddd` to `v1.1.1` ([350e231](https://github.com/geekcell/ddd-symfony-bundle/commit/350e23113c7ad2288529d6083591b6467200411a))
16+
17+
## [1.2.2](https://github.com/geekcell/ddd-symfony-bundle/compare/v1.2.1...v1.2.2) (2023-03-29)
18+
19+
20+
### Miscellaneous Chores
21+
22+
* release 1.2.2 ([c6bfaf5](https://github.com/geekcell/ddd-symfony-bundle/commit/c6bfaf5b3090b9c98601e1a901792386ca35eb0a))
23+
24+
## [1.2.1](https://github.com/geekcell/ddd-symfony-bundle/compare/v1.2.0...v1.2.1) (2023-03-28)
25+
26+
27+
### Miscellaneous Chores
28+
29+
* **ci:** Sonar ([#31](https://github.com/geekcell/ddd-symfony-bundle/issues/31)) ([99e1931](https://github.com/geekcell/ddd-symfony-bundle/commit/99e19313ad89c365b0245baa468852fe12ec5326))
30+
31+
## [1.2.0](https://github.com/geekcell/ddd-symfony-bundle/compare/v1.1.5...v1.2.0) (2023-03-28)
32+
33+
34+
### Features
35+
36+
* release ([#29](https://github.com/geekcell/ddd-symfony-bundle/issues/29)) ([6140643](https://github.com/geekcell/ddd-symfony-bundle/commit/614064397a8818f58c6bdc07c6d6a4a1b3ca5a6e))
37+
38+
## [1.1.5](https://github.com/geekcell/ddd-symfony-bundle/compare/v1.1.4...v1.1.5) (2023-03-13)
39+
40+
41+
### Bug Fixes
42+
43+
* Enable custom doctrine types to handle `null` values ([8f2d52c](https://github.com/geekcell/ddd-symfony-bundle/commit/8f2d52c05fb92220c8c74e3e74b97232c6b8633e)), closes [#26](https://github.com/geekcell/ddd-symfony-bundle/issues/26)
44+
45+
46+
### Miscellaneous Chores
47+
48+
* Apply fixes from PHP CS Fixer and PHPStan. ([4a9ba6b](https://github.com/geekcell/ddd-symfony-bundle/commit/4a9ba6b6f06d3ab256038551413d93e9b295d1ad))
49+
350
## [1.1.4](https://github.com/geekcell/ddd-symfony-bundle/compare/v1.1.3...v1.1.4) (2023-01-20)
451

552

CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# global owners
2-
* @b00gizm
2+
* @b00gizm @janvt

README.md

Lines changed: 148 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
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.
44

5+
---
6+
7+
- [Installation](#installation)
8+
- [Generator Commands](#generator-commands)
9+
- [Building Blocks](#building-blocks)
10+
- [Model & Repository](#model--repository)
11+
- [AggregateRoot & Domain Events](#aggregateroot--domain-events)
12+
- [Command & Query](#command--query)
13+
- [Controller](#controller)
14+
- [Resource](#resource)
15+
516
## Installation
617

718
To use this bundle, require it in Composer
@@ -10,33 +21,91 @@ To use this bundle, require it in Composer
1021
composer require geekcell/ddd-bundle
1122
```
1223

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
1435

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
1947

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).
2149

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.
2351

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
2590

2691
```php
92+
93+
// src/Domain/Event/OrderPlacedEvent.php
94+
2795
use GeekCell\Ddd\Contracts\Domain\Event as DomainEvent;
28-
use GeekCell\DddBundle\Domain\AggregateRoot;
2996

30-
class OrderPlacedEvent implements DomainEvent
97+
readonly class OrderPlacedEvent implements DomainEvent
3198
{
3299
public function __construct(
33-
private readonly Order $order,
100+
public Order $order,
34101
) {
35102
}
36-
37-
// Getters etc.
38103
}
39104

105+
// src/Domain/Model/Order.php
106+
107+
use GeekCell\DddBundle\Domain\AggregateRoot;
108+
40109
class Order extends AggregateRoot
41110
{
42111
public function save(): void
@@ -47,6 +116,8 @@ class Order extends AggregateRoot
47116
// ...
48117
}
49118

119+
// Actual usage ...
120+
50121
$order = new Order( /* ... */ );
51122
$order->save();
52123
$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()
56127

57128
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.
58129

59-
## Repositories
130+
### Command & Query
60131

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.
62133

63-
## Command & Query Bus
134+
#### Generator Command(s)
64135

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
66146

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
68155

69156
```php
70157
// src/Application/Query/TopRatedBookQuery.php
158+
71159
use GeekCell\Ddd\Contracts\Application\Query;
72160

73-
class TopRatedBooksQuery implements Query
161+
readonly class TopRatedBooksQuery implements Query
74162
{
75163
public function __construct(
76-
private readonly string $category,
77-
private readonly int $sinceDays,
78-
private readonly int $limit = 10,
164+
public string $category,
165+
public int $sinceDays,
166+
public int $limit = 10,
79167
) {
80168
}
81169

82170
// Getters etc.
83171
}
84172

85173
// src/Application/Query/TopRatedBookQueryHandler.php
174+
86175
use GeekCell\Ddd\Contracts\Application\QueryHandler;
87176

88177
#[AsMessageHandler]
@@ -96,14 +185,15 @@ class TopRatedBookQueryHandler implements QueryHandler
96185
public function __invoke(TopRatedBookQuery $query)
97186
{
98187
$books = $this->repository
99-
->findTopRated($query->getCategory(), $query->getSinceDays())
100-
->paginate($query->getLimit());
188+
->findTopRated($query->category, $query->sinceDays)
189+
->paginate($query->limit);
101190

102191
return $books;
103192
}
104193
}
105194

106195
// src/Infrastructure/Http/Controller/BookController.php
196+
107197
use GeekCell\Ddd\Contracts\Application\QueryBus;
108198

109199
class BookController extends AbstractController
@@ -124,32 +214,47 @@ class BookController extends AbstractController
124214
}
125215
```
126216

127-
## Supporting Tools
217+
### Controller
128218

129-
### Facades
219+
A standard Symfony controller, but augmented with command and query bus(es).
130220

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
132222

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.
135224

136-
EventDispatcher::dispatch($someEvent);
225+
```bash
226+
Description:
227+
Creates a new controller class
228+
229+
Usage:
230+
make:ddd:controller [options] [--] [<name>]
137231

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
139238
```
140239

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
142241

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)
145243

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
154251

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.
257+
258+
Options:
259+
--config Config flavor to create (attribute|xml).
260+
```

0 commit comments

Comments
 (0)