Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Evolution - Bulgaria PHP UG Meetup

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for Goce Bonev Goce Bonev
January 10, 2023
20

Evolution - Bulgaria PHP UG Meetup

A story about Domain-Driven Design, architecture and working on complex projects…

Avatar for Goce Bonev

Goce Bonev

January 10, 2023
Tweet

Transcript

  1. ▪ Everyone will deal with different kinds of projects in

    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
  2. ▪ Making mostly websites / Facebook games; ▪ Had our

    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
  3. ▪ Small project for a new client; ▪ Started as

    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
  4. ▪ 180 implemented use cases, over 400 more planned; ▪

    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
  5. “A Big Ball of Mud is a haphazardly structured, sprawling,

    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
  6. ▪ Tools and techniques for building software and dealing with

    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
  7. Strategic ▪ Analyze the domain / process of knowledge discovery;

    ▪ 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;
  8. ▪ Ubiquitous language – single language for describing 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
  9. ▪ Eliminating the middle man; ▪ Face to face, casual

    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
  10. Ubiquitous language Ubiquitous /juːˈbɪkwɪtəs/ - constantly encountered, widespread, present, appearing

    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;
  11. Bounded context ▪ Logical boundary within which the ubiquitous language

    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
  12. Simple ▪ Has a CRUD interface; ▪ Described in CRUD

    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;
  13. Core ▪ Provides competitive advantage / strategic investment for the

    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;
  14. Core ▪ Provides competitive advantage / strategic investment for the

    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
  15. ▪ Entity – object with unique identity and a lifecycle,

    (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
  16. ▪ Aggregates are entities enforcing a set of business rules

    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
  17. id int(11) fb_id varchar(255) fb_name varchar(255) tw_image varchar(255) tw_id varchar(255)

    tw_name varchar(255) gp_id varchar(255) gp_name varchar(255) gp_image varchar(255) first_name varchar(255) middle_name varchar(255) last_name varchar(255) phone varchar(255) email varchar(255) address text city varchar(255) state varchar(255) zip varchar(255) country int(11) ship_first_name varchar(255) ship_last_name varchar(255) ship_phone varchar(255) ship_email varchar(255) ship_address text ship_city varchar(255) ship_state varchar(255) ship_zip varchar(255) ship_country int(11) password varchar(255) username varchar(255) image varchar(255) date datetime invoice int(11) invoice_company varchar(255) invoice_cid varchar(255) invoice_vat varchar(255) invoice_city varchar(255) invoice_address text invoice_invividual varchar(255) invoice_phone varchar(255) invoice_email varchar(255) invoice_country int(11) payment varchar(255) bank_title varchar(255) bank_address varchar(255) bank_iban varchar(255) bank_bic varchar(255) newsletter int(11) newsletter_categories int(11) newsletter_sent int(11) group_id int(11) gender int(11) birthday int(11) nameday int(11) referrer_id int(11) active int(11) featured int(11) date_deactivated datetime currency int(11) currency_view int(11) lifetime double share double mtd decimal(10,2) confirmation_code varchar(255) confirmation_sent int(11) password_code varchar(255) password_sent int(11) admin_action int(11) admin_action_date int(11) The ultimate “User”
  18. ▪ Ubiquitous language – made sense; ▪ Focused on the

    tactical part – THE HOW; ▪ Design patterns, design patterns, design patterns; ▪ Entities; ▪ Value objects; ▪ Repositories; ▪ Layered, hexagonal; ▪ Code, code, code…. Strategic what ?!@$# skip that….
  19. ▪ Tactical patterns alone are not DDD - They are

    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 ?!@$#
  20. ▪ Understand the problem domain; ▪ Knowledge discovery; ▪ Ubiquitous

    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
  21. Domain-Driven Design had a steep learning curve! ▪ Requires more

    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
  22. ▪ No isolation or boundaries; ▪ Tight coupling with everything:

    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?
  23. ▪ Push the framework to the edge of the application;

    ▪ 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;
  24. ▪ Database-Driven to Domain-Driven; ▪ Infrastructure first to infrastructure last,

    forget databases, forget persistence; ▪ UI influenced domain model; ▪ Primitive obsession – language primitives to domain objects; ▪ Delay as much decisions as possible till LRM; Mindset
  25. “Software architecture must support the inevitable change without extreme cost

    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
  26. Hexagonal architecture “Hexagonal architecture allows an application to equally be

    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;
  27. ▪ First time doing this; ▪ Overwhelmed, so many new

    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
  28. ▪ Lot of things that need to change; ▪ DDD

    is not an all-or-nothing concept; ▪ Incremental change/add things in small steps; ▪ Gradually introduce DDD thinking, patterns and practices; Small steps
  29. What is it? ▪ Way to rewrite complex systems; ▪

    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
  30. └───Application ├───Console ├───Controllers ├───Dto ├───Models ├───Exceptions ├───Repositories ├───Services └───Utils Folder

    structure └───Application ├───Payments ├───Products │ ├───Application │ │ ├───Command │ │ ├───EventListener │ │ └───Query │ ├───Domain │ │ ├───Events / Exceptions │ │ ├───Entities, VO… │ │ ├───Services │ │ └───Repositories… │ └───Infrastructure │ ├───Console │ ├───Http │ ├───Messaging │ └───Persistence └───Sellers
  31. interface PhotoRepository{ public function nextIdentity() : PhotoId; public function byId(PhotoId

    $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; .... }
  32. READ MODELS != WRITE MODELS -> CQRS? 😡 interface PhotoRepository{

    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; .... }
  33. Microservices ▪ Incremental process; ▪ Bounded contexts at the center;

    ▪ 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;
  34. Event Storming https://www.eventstorming.com/ “EventStorming is a flexible workshop format for

    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;
  35. “EventStorming is a flexible workshop format for 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; Event Storming https://www.eventstorming.com/
  36. “Domain Storytelling is a technique to transform domain knowledge into

    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
  37. ▪ Better code quality / improved security; ▪ Faster feedback

    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)
  38. ▪ Don’t blindly follow trends, microservices, Kubernetes, CQRS, EventSourcing, frameworks,

    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
  39. ▪ Ignoring the language domain experts use; ▪ Any DDD

    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
  40. ▪ Good software requires effective communication between domain experts and

    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
  41. “Once you stop learning, you start dying” — Albert Einstein

    • 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