DEV Community

Domain Driven Design with PHP and Symfony

Ludovic Fleury on June 02, 2020

Personal take on Domain Driven Design with PHP and Symfony framework (2.x to 5.x) This article was written in french here Meaning is eve...
Collapse
 
noverkill profile image
noverkill • Edited

Can you point me to one or more of your github repo(s) where I could see the above concepts implemented in a full / partial application(s)? A simple demo app would be very useful implemented particularly to showcase above concepts. I think share more actual code examples next to the concepts would have been also useful to clarify things. Without actual real life code examples I think this article is too theoretical and only mostly makes sense to people that already very familiar with the concepts you are explaining. One other thing I do not really get, is what happens to the Service layer in DDD, or that's what's replaced with commands?

Collapse
 
ludofleury profile image
Ludovic Fleury • Edited

thank you very much for your constructive feedback. You are totally right. I'll be trying to iterate on them, I don't know if I would edit or post a second one.

The service layer, It is common to discriminate: application service from domain service. It was confusing me when I started. I was used to SOA with a unique service layer.

Basically: Domain service are usually "pure service", like "pure function". Meaning they externalise complexe cross entity logic but they are usually stateless.

For instance: Shipping cost estimation working with an "order", a "shipping address" and a "shipping method". This could be a good domain service. The good part: domain service are easily unit testable. And we like to have domain logic covered by them.

Application (service) layer: these are the handlers in a command pattern approach, and it saved me from so much headaches trying to organise my code.

To be honest, I'm not even quite sure how I would manage a plain DDD project without the command pattern. I would have to implement application service layer to manipulate the persistence, name them (the hardest part) and their methods. Would probably be really messy.

Collapse
 
juanma1331 profile image
juanma1331

Hi im new to backend development and im trying to implement the concepts explained in the article. But how can i catch exceptions thrown by the handlers on my controllers?. I can get returned data from handlers using envelopes, so i was thinking about returning data and error from handlers. What do you think about this? Any suggestion? Plz could you provide with some basic implementation? any repo? Thanks

Collapse
 
ludofleury profile image
Ludovic Fleury

Hi, welcome to backend dev ! I'll hope you will have a rich journey there :)

What you are mentioning is actually a pattern: notification.
Martin Fowler wrote about it there: martinfowler.com/articles/replaceT...

When dealing with a sync command (no rabbitmq etc) I favor intercepting exception directly in the controller, I don't have access to code right now, but something like this, should work:



class MyController
{
  public function xxx() 
  {
    try {
      $this->bus->dispatch($command);
    } catch ( exception )
      // rethrow with appropriate HTTP error code
   }
  }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
juanma1331 profile image
juanma1331

Thanks for your help. ;)

Collapse
 
ferrius profile image
Serge

Hi, thank you for a nice post. Also you can find my implementation of DDD/CQRS there
github.com/ferrius/ddd-cqrs-example

Collapse
 
mohammedabdoh profile image
Mohammed Abdoh

I like your implementation. I have one concern to share with you. Don't you think that the UserInterface for instance is violating the idea of CQRS in common? Because in CQRS you are splitting your writes from your reads and in this case, your repository might only have to have one read method to load the aggregate by its ID while it case persist the aggregate

Collapse
 
ludofleury profile image
Ludovic Fleury

I checked, and it looks clean, here's a DDD / CQRS / ES with Symfony/Doctrine & SQL as event store

github.com/ludofleury/blackflag

Collapse
 
qdequippe profile image
Quentin Dequippe

For you, is stuff related to emails, translation, url generation has to be in Domain? If yes how to handle it?

Collapse
 
ludofleury profile image
Ludovic Fleury

Hi Quentin, as usual « it depends » :)
We always need the context to try to address it.

If you are into translation or email marketing business and e-mail is one of your product, well it’s probably in your core domain, following many rules, invariant and logic.

If you are into ad with url proxy or url shortener with stats or even SEO optimisation business, maybe url generation will be in your core domain.

If not, it might be considered infrastructure. What’s in the domain depends strongly on business requirement about the concept (email, url). Does it needs to « mutate » following an advanced state logic ? DDD might be worth the upfront cost and complexity.

Otherwise simple CRUD does the job.

Collapse
 
yagami271 profile image
Abdallah ismail

can we add setter on command ?

Collapse
 
ludofleury profile image
Ludovic Fleury • Edited

You can do whatever you want! its your implem :)

usually we do not use setter in the "domain" because we do not "set state", a specific process/feature change the state of something.

Yet in command, you can do whatever you want. Its a DTO: en.wikipedia.org/wiki/Data_transfe...

So its just structured data. In my implementation, I use command as a frontier/boundary between user input (weak) & model layer (strong).

So:

  • you always pass primitive to the command (creation).
  • you always get (value) object from the command (usage/execution).

Example: I don't give a DateTime object to my command, I give a date string "2020/01/01" and I get a DateTimeImmutable object from my command.

Why ? because, if you pass it from HTTP or CLI, the user input is a string.

Which brings 2 validations concerns: the input validation and model validation:

  1. I create the command with the date string
  2. I ensure the string looks like a valide date (input validation)
  3. I get the DateTimeImmutable object from my command in my handler (and validate that the date is logic with the requirement of my application)

OR

  1. I get a "Birthday" value object which encapsulate a DateTimeImmutable object (if needed) and in the constructor of this VO, I check that the person is not 350 years old, or at least 18 (according to the requirement of my domain)
Collapse
 
remytheroux profile image
Rémy THEROUX

Great and pragmatic implementation, that's what i like. The bonus track are precious advises, especially on micro service part.