Skip to content

Instantly share code, notes, and snippets.

@Pierstoval
Created April 22, 2026 12:39
Show Gist options
  • Select an option

  • Save Pierstoval/1568329f49a6367f068ce84aa4cef8d8 to your computer and use it in GitHub Desktop.

Select an option

Save Pierstoval/1568329f49a6367f068ce84aa4cef8d8 to your computer and use it in GitHub Desktop.

Friendly Domain Driven Design manifesto

(this is only my views, not a global thing, so if I'm wrong, maybe we are both either wrong or right for someone else anyway, so it's fine, don't need to make a fuss out of harmless opinions)

For experienced DDD architects: why?

I want more people to implement DDD, but in a more friendly way.

The notions between DDD and all others "good programming practices" are widely accessible nowadays, and some devs implement "pure DDD", or "pure hexagonal architecture" and so on.

This has lots of advantages:

  • Easier testability
  • Lowers the possible side-effects
  • Easier for atomic commits
  • And many more

They however have a few disadvantages:

  • Implies more code in many cases
  • Separate config/mapping from the domain, making things a bit less readable (database, serialization, etc.)
  • A lot of abstractions, sometimes so much that it consumes a lot of cognitive load to be understandable
  • Some frameworks or programming languages don't have the inner tooling to be fully "pure" DDD

That's why I have the audacity to put some PHP Attributes from other namespaces (like Infrastructure) in some Domain namespaces: attributes are metadata, and organizing external mapping is way more cumbersome to handle than importing metadata in domain.

This is a compromise that is "not-purely-DDD", but a compromise that definitely increase readability and decreases cognitive load, especially for newcomers.

If you really want to do "pure DDD", know two things:

  • PHP isn't suitable for that in the first place, because it's interpreted, not compiled, even with static analysis. Even datetime, randomness, uuids, many need external libs to do it "the pure DDD way", which makes app's infrastructure way too cumbersome and cluttered for no good reason.
  • I like using Symfony, and though it's quite easy to do it, it can't simplify everything unfortunately. Especially validation, done through Symfony's validator and using Constraints, and even the Validator itself is hard to mock (notably with the execution context...), this means that validation is still done via an external lib. Good luck abstracting all this without reinventing the wheel.
  • Doctrine is not suitable for that either, and there are lots of issues, especially regarding objects mutability, no "final" keyword (because of proxies), and no primitive-type-classes (like "IDs as objects" for instance), and it is good because these are reasons why Doctrine is both powerful and performant, fixing these issues would imply a lot of maintenance burden on the Doctrine Core Team.
  • Newcomers don't want the need for a Ph.D to start coding on a project. There's a balance between "pure design but incomprehensible" and "disorganised clutter quick to work with", that balance is based on pragmatism and compromises.

For new DDD devs

Here is a (grossly reduced and simplified, sorry experts) summary of a few notions that I'd like to introduce in "Friendly DDD projects". Some are (obviously) already existing, some aren't.

  • All contexts have a root namespace: App\{Context}\.... A context (also called "bounded context") is an "abstract feature block", and by viewing at the existing ones, you can directly get a clue of what they are related to.
  • Each context has at most 3 (unless rare exceptions) sub-namespaces:
    • App\{Context}\Infrastructure\: contains code that creates implementations of Domain classes that are tied to the infrastructure, frameworks or external libraries, mostly as "3rd-party-related code". Can only use code from Domain and external dependencies (Symfony, API Platform, Doctrine...)
    • App\{Context}\Application\: contains code that glues different domain-related usages with code from Domain and Infrastructure, and often contains what we call "Use Cases", which correspond to any feature that is called from outside the app and needs to execute a Domain feature.
    • App\{Context}\Domain\: contains all the logic, data and features that are internal to this specific context, and should never use code from outside the Domain. If code here has to refer to another context, should do it by use-ing interfaces, whatever their implementation.
      Exceptions are allowed in complex or special use cases, like:
      • Mapping attributes (for Doctrine, API Platform and Symfony validation, for example).
      • Related entities from other Domains (DDD and Clean architecture usually recommend using ID classes, but Doctrine does not fully support that and has side-effects, and since we're using a relational DBMS, the internal link between two related entities is an ID, so if we have to move to another DBMS for an entity, there will be a versioned migration on the old & new systems that document the proper way to keep the relationship alive after migrating)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment