their career; ▪ Every project is different, everyone will face different problems; ▪ What has worked for us, might not work for you; ▪ There is no one-size-fits-all solution; ▪ Long-term project - 7 years; ▪ Complex medium to large projects (size is relative); ▪ Just sharing experience from real life projects, point A -> B; Disclaimer Icons made by Freepik www.flaticon.com
own content management system; ▪ We relied on magic to cut development costs and deliver fast; ▪ We really liked magic; ▪ We invested heavily in magic; ▪ Magic was our competitive advantage; ▪ Most value, least effort; At the time / 2014 Light vector created by brgfx - freepik.com
simple as I want to have this website where I upload photos and share them with a few people; ▪ 50 use cases, not very complex, mostly CRUD; ▪ Selected a framework we were familiar with to deliver value quickly at a low cost; ▪ 2 developers on the project; ▪ Finished the project on time and budget; ▪ The client was happy; ▪ We were happy; The project
Problem domain was getting really complex; ▪ A lot of time was spent bug fixing; ▪ Thousands of lines of comments in the code; ▪ New feature development was slowing down significantly; ▪ 5 developers working on the project, onboarding was slow; ▪ Slow feedback loops (working code, bugs…), bugs were discovered at runtime; ▪ Cost of new features and changes increased significantly; ▪ Existing infrastructure was maxed out; 6 months later Cost of change Features
sloppy, duct-tape-and-baling-wire, spaghetti-code jungle.” — Brian Foote and Joseph Yoder ▪ Anti-pattern; ▪ No perceivable architecture; ▪ Common due to financial/time pressure, developer turnover, unqualified/inexperienced developers, changing requirements; ▪ Distributed Big Ball of Mud; Big Ball of Mud Image: Tom McFarlin https://bit.ly/3xwBmfR
complexity; ▪ The main focus is on the business problem (domain); ▪ Collaboration between technical and domain experts, direct communication (reduce translation); ▪ Strategic design (what, why) - Bounded contexts, subdomains, ubiquitous language and context maps; ▪ Tactical design (how) – Aggregates, entities, value objects, repositories, services, events, factories, etc.; ▪ No specific architecture, only separation of technical and business concerns; ▪ Usually associated with Layered /Hexagonal architecture, CQRS / ES, event driven – none of these are required; ▪ Goal: Solution that is not over or under engineered and solves the business problem; Domain-Driven Design
▪ Patterns for dealing with problem complexity; ▪ High level design / decisions; ▪ How to decompose systems into components; ▪ How to find boundaries, context; ▪ Define integration patterns; Domain-Driven Design Tactical ▪ Transform discoveries into software architecture and code; ▪ Patterns for dealing with code complexity; ▪ Low level design / decisions; ▪ Write code that reflects the business domain, speaks the languages of the business;
domain, must be reflected in the code; ▪ Domain – The business problem that needs to be addressed; ▪ Subdomains – parts of the business domain; ▪ Bounded context – boundary within which the ubiquitous language is consistent and particular domain rules apply; ▪ Context maps – high level, visual representation of the relationships between the different bounded contexts; ▪ Types of relationships / integration – how to integrate bounded contexts; Strategic DDD
direct communication resulting in maximum knowledge transfer and discovery; ▪ Developers communicating directly with the business / domain experts; ▪ Conversations and collaboration instead of detailed written requirements and documentation; Communication Image: imgflip.com
or found everywhere; ▪ Tool that allows us to talk about the domain model; ▪ Code model == business model; ▪ No mental model translation, business to dev; ▪ Reduced cognitive load; Stock photo library: ▪ Users upload photos they want to sell; ▪ Users add the product version they want to buy in the cart; ▪ Approved photographers list photos they wish to sell; ▪ Customers add the photo license they want to buy in the shopping cart; ▪ As a user… vs As an approved photographer / customer – context lost;
is consistent and particular domain rules apply; ▪ No single unified model ex. The User vs User in a marketplace… user as a seller, user as a buyer, user as a….; ▪ Proper isolation and logical boundaries; ▪ DRY is about knowledge, not code; ▪ Ideally one bounded context models a single subdomain; ▪ Context maps; Integrating bounded contexts ▪ Partnership ▪ Shared kernel ▪ Customer-supplier ▪ Conformist ▪ Anti-corruption layer ▪ Open host service ▪ Published language ▪ Separate ways
terms by domain experts; ▪ Business logic centered around input validation; ▪ Transforms data from one data structure to another; ▪ Low interaction between entities; Assessing complexity Complex ▪ Result of many requirements; ▪ Complicated processes, state transitions, business rules and invariants that have to be protected at all times; ▪ Many distinct execution scenarios; ▪ Complex algorithms and calculations; ▪ High number of interactions between entities resulting in emerging behavior;
business; ▪ High complexity; ▪ Volatile – change often; ▪ Can not be bought or adopted; ▪ Unwise to outsource; ▪ Top talent assigned; Subdomains Supporting ▪ Little to no competitive advantage; ▪ Can be turned into a side business or revenue channel; ▪ Simple; ▪ ETL, CRUD; ▪ Do not change often; ▪ In-house or outsource; Generic ▪ Everyone does them the same; ▪ High complexity; ▪ Battle tested; ▪ Change over time, but not much; ▪ Buy / adopt rather than build;
business; ▪ High complexity; ▪ Volatile – change often; ▪ Can not be bought or adopted; ▪ Unwise to outsource; ▪ Should be maintainable and easy to evolve; ▪ Top talent assigned; Subdomains Supporting ▪ Little to no competitive advantage; ▪ Can be turned into a side business or revenue channel; ▪ Simple; ▪ ETL, CRUD; ▪ Do not change often; ▪ In-house or outsource; Generic ▪ Everyone does them the same; ▪ High complexity; ▪ Battle tested; ▪ Change over time, but not much; ▪ Buy / adopt rather than build; Focusing on the core
(POPO, not Active Records); ▪ Value object – immutable object with no identity, defined by values of attributes. ▪ Aggregates – cluster of closely related objects, under a common parent, creating a consistency boundary. ▪ Domain events –something significant that has happened in the model that may be of interest to other parts of the system. Immutable, published by aggregate roots and domains services; ▪ Domain services – stateless, contain domain logic that does not fit in a single entity or value object; ▪ Repositories – save and retrieve complete aggregates. Protect the domain model from being influenced by storage and infrastructure details; ▪ Factories – Building objects that are too complex or inconvenient to build via constructor methods; ▪ Modules – group classes that relate to a particular bounded context or aggregate / business process together; ▪ Application services – stateless, orchestrate the domain to perform a specific business use case, do not contain business logic; Tactical patterns
that must remain consistent at all times; ▪ Transactional consistency boundary; ▪ Change one aggregate per transaction; ▪ No child updates from the outside; ▪ Global / local identity; ▪ Reference other aggregates only by identity; ▪ Update other aggregates using eventual consistency; Aggregates Link: https://github.com/ddd-crew/aggregate-design-canvas
just the HOW. The more important parts are WHAT and WHY; ▪ The real power of DDD comes from strategic design, bounded contexts, context maps, ubiquitous language; ▪ DDD is about collaboration and discovery, understanding the domain; ▪ Most valuable DDD work is done on whiteboards, over coffee / drinks, through conversations with domain experts; ▪ DDD is about discovering what you need to write, why you are writing it, where to focus, where to go easy; ▪ Don’t waste energy on the wrong parts of the system; Don’t unnecessarily complicate stuff; ▪ The “big guns” are for strategically important core domains; ▪ For low-complexity, supporting subdomains, CRUD / Active Record / transaction script are OK; ▪ All patterns and principles are just guides, understand when it is ok to break the rules; We got it wrong ?!@$#
language; ▪ Strategic architecture: ▪ Identify business subdomains (decompose); ▪ Asses the complexity of each subdomain; ▪ Find the core domain; ▪ Create boundaries and define responsibilities; ▪ Map the communication and interaction between subdomains; ▪ Decide how much effort should be put in each bounded context (keep it lean); ▪ Implement bounded contexts using the right architecture and patterns; Strategic to tactical - flow
than just development skills; ▪ You actually need to talk to people; ▪ Real experience with OOP; ▪ Builds on top of design patterns and principles; ▪ SOLID, YAGNI, KISS, DRY; ▪ Composition over inheritance, encapsulation, abstractions; ▪ A good IDE; ▪ Practice / trial and error; The requirements Image: Jessica Menke https://bit.ly/30tqbIl
framework, database, between models and services; ▪ Anemic domain model / ghost updates; ▪ Debugging primitives was taking a lot of time; ▪ No implicit transactions / bad transaction management / data corruption; ▪ Dependency management issues, service locator pattern, static facades; ▪ Deployment pipeline = shell script that renames folders; ▪ So many other thing… ▪ Fragile system; What actually was wrong?
▪ Ideally no framework past controllers; ▪ Eloquent models should become POPOs (Plain old PHP objects); ▪ All persistence operations must go through repositories; ▪ Clean up the service layer; ▪ Move business rules from services down to the domain model; ▪ Model implicit concepts explicitly; ▪ Reduce primitive obsession. ▪ Make immutable everything that can be; On a microlevel ▪ Rely on interfaces instead of concrete implementations; ▪ Abstract the infrastructure, storage; ▪ Use proper dependency injection; ▪ Concurrency safety; ▪ Start to use the ubiquitous language, communication and code; ▪ New way of physically organizing code / folder structure; ▪ Build a deployment pipeline;
forget databases, forget persistence; ▪ UI influenced domain model; ▪ Primitive obsession – language primitives to domain objects; ▪ Delay as much decisions as possible till LRM; Mindset
and effort.” Vaughn Vernon – Strategic Monoliths and Microservices ▪ Provide a good foundation with minimal overhead; ▪ Flexible for future changes in use cases and requirements (business-driven changes); ▪ Ecosystem change – technologies, libraries, frameworks, tools, environments; ▪ Low cost of delay / options – delay taking important decisions for as long as possible, last responsible moment; ▪ Good developer experience, collaboration; Architecture
driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases.” – Alistair Cockburn ▪ Swappable components, swap implementations via dependency injection without changing the business logic (plug and play); ▪ Allows postponing technology decisions till the LRM; ▪ Testability, components can be tested in isolation; ▪ Domain does not depend on anything, application depends only on domain;
concepts, ideas…; ▪ No real PHP project we can use as a reference; ▪ Used IDDD as a guide; ▪ Translated Java code from IDDD to PHP; ▪ Small test project to figure things out; Challenges
is not an all-or-nothing concept; ▪ Incremental change/add things in small steps; ▪ Gradually introduce DDD thinking, patterns and practices; Small steps
Incremental migration of features and functions; ▪ Reversable; ▪ Reduced risk vs full rewrite; How does it work? ▪ Identify old or new functionality; ▪ Implement the functionality using the new way (different coding style, architecture, microservices); ▪ Leave the existing functionality as is and route traffic to the new system; ▪ Remove the old code; Strangler fig Image: Diana Darie https://bit.ly/3HyiuBF
$id) : Photo; public function store(Photo $photo) : void; public function remove(Photo $photo) : void; public function all() : PhotoCollection; public function active() : PhotoCollection; public function scheduledForDeletion() : PhotoCollection; public function uploadedByUser(UserId $userId) : PhotoCollection; public function relatedTo(PhotoId $id) : PhotoCollection; .... }
public function nextIdentity() : PhotoId; public function byId(PhotoId $id) : Photo; public function store(Photo $photo) : void; public function remove(Photo $photo) : void; } interface PhotoQueryService{ public function all() : PhotoReadModelCollection; public function active() : PhotoReadModelCollection; public function scheduledForDeletion() : PhotoReadModelCollection; public function uploadedByUser(UserId $userId) : PhotoReadModelCo... public function relatedTo(PhotoId $id) : PhotoCollection; .... }
▪ Macroservices, right-sized services; ▪ Physical boundaries to enforce clean models (ex. each BC with his own DB); ▪ Communication via explicit contracts (API, REST, Messaging…); ▪ Modular monolith; ▪ Start with logical separation instead of physical; ▪ Refactoring logical boundaries costs less than refactoring physical ones; ▪ Supported by chosen architecture;
collaborative exploration of complex business domains.” ▪ Big picture - for project kick-offs; ▪ Process modelling – focused on a single process; ▪ Software design – deeper into the technical part of the process;
complex business domains.” ▪ Big picture - for project kick-offs; ▪ Process modelling – focused on a single process; ▪ Software design – deeper into the technical part of the process; Event Storming https://www.eventstorming.com/
effective business software. It brings together domain experts and development teams.” ▪ Visual, collaborative, domain modeling technique; ▪ Uses icons for actors and work objects, joined by activity arrows; ▪ Domain stories told from the actor’s perspective; Domain Storytelling Image and quote: domainstorytelling.org
loops - TDD / unit tests / static code analysis tools (low late bug discovery); ▪ Feedback from tests gave us early “sense of progress” on large complex features; ▪ Code itself tells the whole story; ▪ Reduced amount of unplanned work; ▪ Better technical debt management / lower technical debt; ▪ Make changes without fear of breaking things - resilient code; ▪ Allowed the app to evolve; ▪ Onboarding new developers was easier; ▪ Framework independent, database / infrastructure-independent; ▪ Delay architectural decisions, base them on concrete requirements, not wild guesses; ▪ Deploy with confidence; ▪ Less stress, more happiness 😊; Long-term results (technical)
etc… ▪ Same for patterns; ▪ Use technology based on your actual needs not what’s cool at the moment; ▪ Everything is valid in its own context; ▪ Each technology or pattern solves a different problem, and it might not be your problem; ▪ You don’t know it all; ▪ Have an open mind; ▪ Critical thinking; ▪ The five whys; Don’t be dogmatic
for CRUD subdomains; Real DDD for non-core subdomains; ▪ Using only the tactical patterns. DDDLite middle-ground, either up to real DDD or down to something simpler; ▪ Not identifying bounded contexts / context maps; ▪ No iterative refinement. Failing to recognize the domain is evolving, bounded contexts change; ▪ No isolation, polluting the domain with external concepts (infrastructure or another 3rd party domain model); ▪ No access to domain experts / proxy domain experts (BA); ▪ Wasting experts time on generic subdomains; ▪ Forgetting DDD is expensive; ▪ Not providing enough value; Mistakes, fails and pitfalls
developers; ▪ Good coding practices and architecture matter; ▪ Writing tests last sucks; ▪ There is no one-size-fits-all solution, there is no “best practice / best solution”; ▪ There is no such thing as a free lunch; ▪ Everything is a compromise, you get something, you loose something; ▪ Hexagonal is flexible, allows early and late decisions with minimal overhead; ▪ Before thinking about microservices, learn how to build good monoliths; ▪ Strategic design is essential for all microservices, tactical modeling is not; ▪ Solve the problem, don’t be constrained by frameworks, technologies, architecture….; ▪ Change is inevitable, embrace it; ▪ Try new things, experiment; ▪ Continuous learning; Lessons learned
• Technology getting more complicated every day; • List of things you are expected to know grows every day; • Different tech and skills for the next 1, 2…5 years; • The moment you stop learning, your value decreases; • Try new languages, explore new technologies or for some keep up with frameworks, but focus on things with long term value; • Continuous improvement; Continuous learning