Skip to content

Commit 21bd14c

Browse files
committed
[Docs] DDD
1 parent 417ff5b commit 21bd14c

8 files changed

Lines changed: 254 additions & 0 deletions

File tree

40 KB
Loading

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ The Sylius stack is a set of tools for your Symfony projects:
1919
----------------
2020

2121
* [How to customize your admin panel](cookbook/admin_panel/index.md)
22+
* [How to use in a DDD architecture](cookbook/ddd_architecture/index.md)

docs/SUMMARY.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
* [Customizing the logo](cookbook/admin_panel/logo.md)
1111
* [Customizing the menu](cookbook/admin_panel/menu.md)
1212
* [Configuring the security access](cookbook/admin_panel/security.md)
13+
* [How to use in a DDD architecture](cookbook/ddd_architecture/index.md)
14+
* [Architecture overview](cookbook/ddd_architecture/overview.md)
15+
* [Resource configuration](cookbook/ddd_architecture/resource_configuration.md)
16+
* [Basic operations](cookbook/ddd_architecture/basic_operations.md)
1317

1418
## Admin UI
1519

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Basic operations
2+
3+
## Book creation
4+
5+
<div data-full-width="false">
6+
7+
<figure><img src="../../.gitbook/assets/create_book_resource.png" alt="Create book resource"></figure>
8+
9+
</div>
10+
11+
In the Application folder, we already have this "CreateBookCommand":
12+
13+
```php
14+
// src/Bookstore/Application/Command/CreateBookCommand.php
15+
declare(strict_types=1);
16+
17+
namespace App\BookStore\Application\Command;
18+
19+
use App\BookStore\Domain\Model\Book;
20+
use App\BookStore\Domain\ValueObject\Author;
21+
use App\BookStore\Domain\ValueObject\BookContent;
22+
use App\BookStore\Domain\ValueObject\BookDescription;
23+
use App\BookStore\Domain\ValueObject\BookName;
24+
use App\BookStore\Domain\ValueObject\Price;
25+
use App\Shared\Application\Command\CommandInterface;
26+
27+
/**
28+
* @implements CommandInterface<Book>
29+
*/
30+
final readonly class CreateBookCommand implements CommandInterface
31+
{
32+
public function __construct(
33+
public BookName $name,
34+
public BookDescription $description,
35+
public Author $author,
36+
public BookContent $content,
37+
public Price $price,
38+
) {
39+
}
40+
}
41+
```
42+
43+
The idea is to reuse this command to create the book in the storage for your "create" operation.
44+
45+
```php
46+
// src/BookStore/Infrastructure/Sylius/Resource/BookResource.php
47+
// ...
48+
use App\BookStore\Infrastructure\Sylius\State\Processor\CreateBookProcessor;
49+
use App\BookStore\Infrastructure\Symfony\Form\BookResourceType;
50+
use Sylius\Resource\Metadata\Create;
51+
// ...
52+
53+
#[AsResource(
54+
// ...
55+
formType: BookResourceType::class, // Define the form type for all your operations
56+
operations: [
57+
new Create(
58+
processor: CreateBookProcessor::class,
59+
// formType: CreateBookResourceType::class, // Optional: define a specific form type only for the "create" operation
60+
),
61+
],
62+
)]
63+
final class BookResource implements ResourceInterface
64+
{
65+
// ...
66+
}
67+
```
68+
69+
Now you need to write your CreateBookProcessor.
70+
71+
```php
72+
// src/BookStore/Infrastructure/Sylius/State/Processor/CreateBookProcessor.php
73+
74+
namespace App\BookStore\Infrastructure\Sylius\State\Processor;
75+
76+
use App\BookStore\Application\Command\CreateBookCommand;
77+
use App\BookStore\Domain\ValueObject\Author;
78+
use App\BookStore\Domain\ValueObject\BookContent;
79+
use App\BookStore\Domain\ValueObject\BookDescription;
80+
use App\BookStore\Domain\ValueObject\BookName;
81+
use App\BookStore\Domain\ValueObject\Price;
82+
use App\BookStore\Infrastructure\Sylius\Resource\BookResource;
83+
use App\Shared\Application\Command\CommandBusInterface;
84+
use Sylius\Resource\Context\Context;
85+
use Sylius\Resource\Metadata\Operation;
86+
use Sylius\Resource\State\ProcessorInterface;
87+
use Webmozart\Assert\Assert;
88+
89+
/**
90+
* @implements ProcessorInterface<BookResource>
91+
*/
92+
final readonly class CreateBookProcessor implements ProcessorInterface
93+
{
94+
public function __construct(
95+
private CommandBusInterface $commandBus,
96+
) {
97+
}
98+
99+
public function process(mixed $data, Operation $operation, Context $context): BookResource
100+
{
101+
Assert::isInstanceOf($data, BookResource::class);
102+
103+
Assert::notNull($data->name);
104+
Assert::notNull($data->description);
105+
Assert::notNull($data->author);
106+
Assert::notNull($data->content);
107+
Assert::notNull($data->price);
108+
109+
$command = new CreateBookCommand(
110+
new BookName($data->name),
111+
new BookDescription($data->description),
112+
new Author($data->author),
113+
new BookContent($data->content),
114+
new Price($data->price),
115+
);
116+
117+
$model = $this->commandBus->dispatch($command);
118+
119+
return BookResource::fromModel($model);
120+
}
121+
}
122+
```
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# How to use in a DDD architecture
2+
3+
In a Domain-Driven Design architecture, there are no defined structures.
4+
Here we present a lot of tips to help you to integrate the Sylius stack components in your clean architecture.
5+
6+
In this cookbook we reuse the apip-ddd project from Mathias Arlaud and Robin Chalas for their excellent __Domain-driven design with API Platform 3__ talk.
7+
8+
{% embed url="https://youtu.be/SSQal3Msi9g?si=L9u7tvhH4qGBpiqj" %}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Architecture overview
2+
3+
Here is the folder structure of the "BookStore" domain:
4+
5+
```txt
6+
src
7+
└── BookStore
8+
├── Application
9+
├── Domain
10+
└── Infrastructure
11+
└── Sylius
12+
```
13+
14+
* The `src/BookStore/Domain` directory contains your business code
15+
* The `src/BookStore/Application` contains your application code that is not related to any framework/package
16+
* the `src/BookStore/Infrastructure/Sylius` directory contains everything that is related to Sylius packages
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Resource configuration
2+
3+
## Define The Sylius Book Resource
4+
5+
```php
6+
// src/BookStore/Infrastructure/Sylius/Resource/BookResource.php
7+
8+
namespace App\BookStore\Infrastructure\Sylius\Resource;
9+
10+
use App\BookStore\Domain\Model\Book;
11+
use App\BookStore\Infrastructure\Symfony\Form\BookType;
12+
use Sylius\Resource\Metadata\AsResource;
13+
use Sylius\Resource\Model\ResourceInterface;
14+
use Symfony\Component\Uid\AbstractUid;
15+
use Symfony\Component\Validator\Constraints as Assert;
16+
17+
#[AsResource(
18+
section: 'admin',
19+
templatesDir: '@SyliusAdminUi/crud',
20+
routePrefix: '/admin',
21+
driver: false,
22+
)]
23+
final class BookResource implements ResourceInterface
24+
{
25+
public function __construct(
26+
public ?AbstractUid $id = null,
27+
28+
#[Assert\NotNull(groups: ['create'])]
29+
#[Assert\Length(min: 1, max: 255, groups: ['create', 'Default'])]
30+
public ?string $name = null,
31+
32+
#[Assert\NotNull(groups: ['create'])]
33+
#[Assert\Length(min: 1, max: 1023, groups: ['create', 'Default'])]
34+
public ?string $description = null,
35+
36+
#[Assert\NotNull(groups: ['create'])]
37+
#[Assert\Length(min: 1, max: 255, groups: ['create', 'Default'])]
38+
public ?string $author = null,
39+
40+
#[Assert\NotNull(groups: ['create'])]
41+
#[Assert\Length(min: 1, max: 65535, groups: ['create', 'Default'])]
42+
public ?string $content = null,
43+
44+
#[Assert\NotNull(groups: ['create'])]
45+
#[Assert\PositiveOrZero(groups: ['create', 'Default'])]
46+
public ?int $price = null,
47+
) {
48+
}
49+
50+
public function getId(): ?AbstractUid
51+
{
52+
return $this->id;
53+
}
54+
55+
public static function fromModel(Book $book): self
56+
{
57+
return new self(
58+
$book->id()->value,
59+
$book->name()->value,
60+
$book->description()->value,
61+
$book->author()->value,
62+
$book->content()->value,
63+
$book->price()->amount,
64+
);
65+
}
66+
}
67+
```
68+
69+
## Define The Symfony Book Resource Form Type
70+
71+
```php
72+
// src/BookStore/Infrastructure/Symfony/Form/BookResourceType.php
73+
74+
namespace App\BookStore\Infrastructure\Symfony\Form;
75+
76+
use App\BookStore\Infrastructure\Sylius\Resource\BookResource;
77+
use Symfony\Component\Form\AbstractType;
78+
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
79+
use Symfony\Component\Form\FormBuilderInterface;
80+
use Symfony\Component\OptionsResolver\OptionsResolver;
81+
82+
final class BookResourceType extends AbstractType
83+
{
84+
public function buildForm(FormBuilderInterface $builder, array $options): void
85+
{
86+
$builder
87+
->add('name')
88+
->add('author')
89+
->add('price')
90+
->add('description')
91+
->add('content', TextareaType::class)
92+
;
93+
}
94+
95+
public function configureOptions(OptionsResolver $resolver): void
96+
{
97+
$resolver->setDefaults([
98+
'data_class' => BookResource::class,
99+
]);
100+
}
101+
}
102+
```

docs/cookbook/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# Cookbook
22

33
* [How to customize your admin panel](admin_panel/index.md)
4+
* [How to use in a DDD architecture](ddd_architecture/index.md)

0 commit comments

Comments
 (0)