Domain Driven Design (DDD) made plain broke down to the bone gristle. cus i need that science to level up my code, but miss me with all the stuffy acronyms and jargon. π§΅
domain
- the actual problem
- what we're trying to solve with the code for the user
- the set of problems that the users ask the developers to solve
- the subject matter
domain driven
- problem focused
- to stay focused on the actual problem
design
- code
- to arrange the code so that it solves a problem
- the code setup that solves the users' problem
domain driven design
- coding to solve the users' actual problem not to geek out on tech
- making all coding decisions based on the actual problem
- keeping the code in line with the actual problem
- problem focused coding
- tech for the users' sake
#ddDesign
the usual stress
- anti-domain driven design
- coding to geek out on the language, framework, ide, buzzword
- making all coding decisions based on the new tech buzzword
- keeping the code dipped in the new shiny tech
- coding for teh tech
- tech for tech sake
ddd benefits
code that:
- reads the way the users talk about the problem
- shows how each line directly helps the user
- makes sense to read; that doesn't look like pure geek-speak
- is as changeable/flexible as your understanding, ideas, pov of the business
ddd benefits (cont)
code that:
- is accurate and directly related to the users' problem
- is rich in meaning to current devs and future devs
- becomes legacy code that future developers will love
- is valuable even after the original tech, framework, platform has come and gone
rave n rant side bar
don't let the the tech interview quizzes, the job descriptions, the gatekeeper shit fool you
the whole point of coding is to solve the problem that the users want solved.
users don't gaf about code or fizz buzz or k8s. they want their problem solved
the code (lang, ide, framework, platform, buzzword, etc) is not the point - it's just the tool
the code != the subject matter != the problem != the point
solving the users' problem is the whole point of all the coding and tools
the blue book
"Domain Driven Design Tackling the Complexity in the Heart of Software" by Eric Evans
btw if yall havent already read the blue book aka the ddd bible, please do yourself a favor and go cop that
amazon.com/Domain-Driven-β¦
not for nothing
"Domain-Driven Design Distilled" by Vaughn Vernon
aka the green book, is another one. i aint read it yet, but Vernon is a good dude that i actually got to build w/ a bit on twitter, and people rave about it
amazon.com/Domain-Driven-β¦
ok but back to it. so that breaks down the basic whats and whys of Domain Driven Design, with hopefully a lot of the jargon made plain. but there's of course a lil bit more to the science...
ok so far so good. that was a basic intro to the ddd game. if you noticed, all i did so far was chop up the name. but as we can see, just those 3 lil Ds pack a whole lotta meaning
but let's dig just a lil deeper
ddd stands on two legs, the first is
Model Driven Design
let's make it plain the same way we did DDD above
model
- an understanding, or point of view (pov)
- a set of thoughts and ideas about something
- to create, draw out, write up, describe
- to develop a pov
- a (mind) map, or to create one
domain model
- shared understanding
- a shared understanding of the actual problem
- a set of thoughts and ideas used for understanding
model driven
- focused on shared understanding
- to be focused on the shared understanding of the problem, not teh tech
model driven design (MDD)
- coding focused on shared understanding
- coding to the shared pov of the users' actual problem
- making all coding decisions based on shared understanding of the actual problem
- keeping the code in line with the shared pov of the actual problem
knowledge crunching (MDD bonus)
aka - mad question asking; a ton o back n forth btwn users n devs
- getting to the bottom of the problem
- learning the quirks that are not obvious at first
- overstanding the actual needs of the users
- pulling the gems outta the subject matter
so that's the MDD definition put plain, but like i said ddd stands on two legs. the second leg is, and imo the most important:
Ubiquitous Language
this one's easy to break down but it's sooo important
ubiquitous language (UL)
- shared vocab
- voice of the "model" which is the "shared understanding of the actual problem"
- glues every line of code, and all team activity - to the "model"
- keeps the "model" in the code, and at the tip of your tongue/fingers
one team one language (UL cont)
- share vocab used by the:
- developers talk about the system with the business people using the same vocab
- business people and users talk to the devs using the same vocab
feedback loop #dddfl (UL/MDD cont)
π shared vocab
π shared understanding
π way the code is named/arranged in the system
π shared understanding
π shared vocab
btw π meaning - "feeds into" or "comes out of"
if ddd was a tree, this ubiquitous language and model driven design would form the root. from this base we can play with all the fun stuff in ddd
start to look at your code base and ask new questions?
is it dealing with the domain?
your teams - are they knowledge crunching and modeling?
the users - are we all speaking in the UL?
and is all of that in the code?
imho, these concepts alone will give you a decent first handle on ddd. but wait, there's more you say? oh don't worry there's more to come
side bar
btw all im doing here is ddd modeling out loud in tweets
"Find easier ways to say what you need to say, and then take those new ideas back down to the diagrams and code. (and tweets)" - Eric Evans
just to harp one of the most important things in ddd:
communication!
1) conversations between the users and devs back n forth (knowledge crunching)
2) in the shared vocab (ubiquitous language)
3) into the feedback loop #dddfl
this communication makes or breaks ddd!
devs talk to the users talk to the devs
users
- the customer, client, users, or those who represent them
- the gurus of the actual problem (domain experts)
devs
- coders, architects, devops ppls, DBAs etc
- we have the tech skill and know how
we need constant collab
communication
prolly the toughest thing to get right in ddd. for this to work properly the team needs to be down for the back and forth. crunching knowledge of the problem.
if the team's not with this, then it ain't solving the real problem
ddd is about the vocab, ideas, concepts, events, usage situations that are important to the actual users - the problem to be solved - the domain
bouncing questions off of each other, repeatedly from different angles, at different days/times, in different conversations, w/ different people
this is how you get to the domain i.e. the root of the problem that the users need solved
throwing around ideas, povs, interpretations, understandings, concepts between the users and devs
both groups need to be willing and able to be *constantly* adding to the #dddfl and always using the shared vocab (ubiquitous language UL)
communication/collab styles
- the ddd whirlpool is an effective knowledge crunching collaboration style
- event storming is the most popular way of achieving this that i know of
def needed to harp on that point a bit. it's key. now on to some building blocks
layered architecture
- code base organization
- classes grouped by what they're for, their "concerns"
- they call this "separation of concerns"
- the KEY CONCERN - to protect/isolate the heart
* heart = classes that are for expressing the domain
layers in typical application
1) UI layer
- for user input/output handling
2) application layer
- for collab between other other layers
3) domain layer
- for expressing the problem subject matter; the heart
4) infrastructure layer
- for the tech/device input/output handling
layers keep your code base organized - a huge topic all by itself, and there's a lil more to it e.g. they talk to each other in a top down fashion?
for more on how to find the un/tidiness in a code base - def do ya googles on "hexagonal architecture" and "clean architecture"
smart ui
- pattern used for starter projects, or small, simple apps
- can be used as a quick proof of concept or prototype
- ui, application, infrastructure layers combined into one layer
- still - very important to *keep the domain model layer separate*. protect the heart
so that's layers and smart ui. very important basic building blocks of any app.
now i wanna quickly refresh "domain model" because it is the heart of the software and we'll reference it more often in touching on the next building blocks (or patterns)
domain model (revisited)
- shared understanding
- a shared understanding of "the actual problem" (not of teh tech)
- a set of thoughts and ideas used for understanding the actual problem
- the heart of the code base
- the thing we want to express loud in all our code
now to get into more building blocks, what you would call the "domain model" classes.
these would be the actual nuts-and-bolts classes or structures in the code. and they come in different flavors:
value, entity, service, aggregate, factory, repository, module
value object (vo)
- a class for describing "what" a domain concept is
- doesn't need to represent any identity concept
- as always, named after words in the shared vocab
- express meaning via their fields and methods
entity
- a vo that describes a domain concept with ID
- expresses an "identity" domain concept
- reps a concept that has a meaningful identity
- draw out the "who"s and the "which"s of the domain model - e.g. Car (vin#), Person (ssn#)
service
- class focused on carrying out some "activity"
- focused on needed activity concepts (methods) in the domain
- when a method don't make sense as part of any vo/entity; put em in a service
- holds loose activity/methods in the classes where it makes sense to hold em
aggregate (agg)
- group of classes seen together as 1 clean/clear domain concept
- objects in it, make sense being handled together as 1
- made up of a root (entity) + other entities/VOs
- all activity comes in/out the root of the agg
- the agg stays whole/together at all times
factory
- class focused on logic for re/building other whole objects
- builds values, entities, aggregates
- rebuilds objects after pulling em from a db, browser, or other device
repository
- class for presenting a "collection" or "list" of other kinds of objects - usually aggregates
- "collection" domain concept
- provides a look n feel that speaks in shared vocab
- simplifies working with a db or some data store
module
- a folder/package
- hold classes that makes sense together based on the domain model
- like layers we talked about before - modules help organize code
- keeps similar things together, different things apart
- helps make code base organized and understandable
Staying on code (pun intended)
Again, let's remember - the main thing in ddd is to "express" the shared understanding (aka domain model) - All the fancy building blocks, patterns mentioned above, are only there for that reason.
So let's talk about this "expression".
Expression (code speaks)
How can code can "express" the domain model - "speak", talk like, describe, or capture - ideas and understanding?
It's hard with words, much less code. It's a deep topic, but the following is a plain description that's been useful to me.
Code speaks
- name all class fields, params, vars after nouns in the shared vocab
- name all class methods after verbs in the shared vocab
- keep the code in context
- design/arrange code objects to stay in context of what happens in real life the way the users' describe it
Code speaks (noun, verbs ...)
for example, a "car":
- has doors, wheels, engine, vin as fields
- has drive(), brake(), park() as methods
- if you call it "vehicle" but the users call it "car" - you should prolly call it "Car"
it's Objects 101, but we still also need "context"
Code speaks (context)
Picture a "car" in these contexts:
- on route 1
- at the dealership
- disassembled in the factory
- as part of a passenger train
- at a gas station car wash
Imagine the various ways you'd need to code each car. That's design and coding for context.
Code speaks (expression)
in a nutshell - code + devs + users all speak in the same vocab #dddfl
things in the code:
1) are named after words and terms in the shared vocab
2) interact in a way that makes sense in the context you're dealing with in the application
expression
this quick summary has been very useful for me up to now, but the topic's been written about, by people way smarter than me, in classic text books n crap. so def do ya googles. (see: object oriented philosophy #oop)
Expressive design, and speaking code, is the perfect lead in to my favorite part of ddd - Refactoring
Refactoring
- improving code
- redesigning code without changing what it does
- renaming fields, methods, classes, packages, etc
- make the code base easier to code in
- done as part of normal coding
- done every time you touch the code base
Refactoring refactoring
- refactoring for "faster" or maybe "less lines of" code is one thing. but it's even better when you refactor in order to:
* "express" the domain model better *
keep refactoring the code so it speaks the shared vocab, expressing the shared understanding
Refactor the low key
- make the low key, high key
- make the implicit, explicit
- spell it out. Loud.
Refactor the low key (cont)
e.g. - it's easy to take for granted that a car "door" also has "windows" that go up and down
but if it's an idea in your head, it should be in the code
e.g. both "door" and "window" would be fields or value objects as part of a "car" aggregate
Refactoring knocks
crisis as opportunity (refactor)
- when the code feels off, or plain wrong - it's a good thing - it's begging you to refactor it
- opens the door for deeper meaning/expression
- usually this'll burst open the model/ understanding for even better refactorings
the model is always changing
refactoring is not a one time thing. it happens every time you touch the code. coding is design is refactoring, one in the same.
so there is always the chance to leave your mark and make the code better.
on that note, let's get into some design guidelines or patterns next
supple design
a codebase that
- gives you the "soft" in software
- is ready for change at any moment without strenuous effort
- "fits" the shared understanding like a glove
- is organized so that you love to work in it
- is agile, adaptable, moldable
some patterns below...
intention revealing interface
descriptive and meaningful interface names
"interface" types:
- var/method/class/etc
- method signature
- set of public methods (API)
- interface file/class
* name them after the shared vocab
* let them describe what they do (but not how)
side-effect-free functions
keep functions functions
2 method kinds:
1) function: return results
2) modifier: is void, but "changes state" i.e. sets values (side-effects)
- let functions be that i.e. return results - no side effects
- if it behaves as both, split it in 2
standalone class
- remove unneeded refs to other classes
- break down a class til it makes sense by itself
- it's easier to understand independent classes that can stand alone
- favor primitives (int, string, etc) for fields
* replace "class" with "concept" n reread
closure of operations
make method return val, and param/s, the same type
- minimize what u have to keep track of mentally
- avoid needlessly piling extra concepts/classes onto the convo/module
- keep class focused on single train of thought
- applies to value object methods
assertion
- some methods creep - produce side effects on the low
- call out the state of the app or class/agg, before and after the creep method call (pre/post conditions)
- make them obvious:
* w/ "assert" keyword or exceptions in code
* in unit tests, or docs readme/ wiki
conceptual contour
the ebbs n flows of a live domain and codebase
expect concepts to:
- evolve at diff rates,
- come in diff shapes, sizes, complexity, importance
* keep like concepts together, diff ones apart
* now replace "concept" with "class" or "module" n reread
context
area of meaning
- you can look at the same system from different povs (models)
- so context is key; it makes or breaks designs in big projects
- different areas of the system, give different meanings to the same things
* see "code speaks" section from before
staying in context
a large codebase will have *several* povs (models) in it
think of povs of a "car"
- on a "racetrack"
- then in a "garage"
or that "garage"
- in a "house"
- or an "office bldg"
they're all very different concepts. the differences are KEY in the codebase
[un]bounded context
when areas of meaning (contexts) are not seen clearly (bound)
- there will be bugs, dupes, confusion, and extra work
- usually in 2 flavors:
1. duplicate concepts
2. false cognates
[un]bounded context (duplicate concepts)
two different classes, used in the same way
e.g. we use a Settings class - other devs use an Account class elsewhere - but both classes actually represent the same "user profile" concept. so they're easy to get outta sync w/ each other
[un]bounded context (false cognate)
one class used in 2+ different ways
e.g. we use a Person class as "player" - other devs use the same Person class as "user" - (cus Person seems like the same concept, taken outta context) - but "player" n "user" mean two very different things
bounded context
define your system's areas of meaning (subsystems) - lines can (or may already) be drawn, using separate:
- project codebases/repositories
- database schemas
- teams' policies and points of contact
* use #conwaysLaw don't let it use you
keep things in context
bounded context (cont)
the idea is to look at your system
- at the lines drawn around the areas of diff meanings within the domain
- draw new lines where they're lacking
- let those determine teh tech
* lines drawn by making use of separate git projects, dbs and #conwayslaw
bounded context (quick def)
a smaller "part" of a larger system "whole" that needs to be made into it's own subsystem/app (cus it has distinct meaning)
and seen and treated in the org as such, because there's a designated team and codebase that deals with that "part"
bounded context (you're on one)
if you're on a team working on a large system OR you've ever cloned anything that sounded anything like "microservices"
then the code you are working on is in a bounded context AND you probably switch between multiple contexts daily
continuous integration (CI)
to keep development together on track, use tools/policies for frequent automated feedback from:
- unit/integration tests
- commit, merge, test, etc
* the use of the shared vocab (UL) in every convo
cus without CI, contexts tend to wanna split
context relationship patterns
while we separate contexts so that they:
- are clearly seen, explicit
- each have their own organized space
- limit duplication or confusion
still, they need to "relate" as part of a whole
there are seven general kinds of relationships...
1. separate ways
* the two contexts (apps/teams) apps can operate independently
- little to no communication/coordination necessary between the two contexts
2. anticorruption layer
- two contexts (apps/teams) need to collab but their models/vocab/users are very different
* they aim to keep their models from becoming confused with the other
- each adds a layer of code to simplify, translate, wall-off the model/vocab of the other
3. conformist
- two contexts (apps/teams) need to cooperate but one of them dominates
- one context operates in a way that forces the actions of the other for whatever technical/business/political reason
* so one context (app/team) conforms to whatever the other one needs
4. published language
- two contexts (apps/teams) need to collab but translation is heavy
- each context speaks different vocab/code/model; but don't wanna maintain the translation rules
* create a new language or translation impl; publish it for the other contexts to use it
5. open host service
- a context (app/team) needs to collab with multiple others
* define simple services, instead of complicated translators for each guest
- a set of services is simpler than custom translators
- especially when the service logic is shared across guests
6. customer supplier teams
- two contexts (apps/teams) need to coordinate
- independent teams but one is upstream e.g. front end, one is downstream e.g. backend
* need frequent code commits/merges integration, collaborative dev team planning, meetings
7. shared kernel
- two contexts (apps/teams) need to mostly share one model/codebase/vocab
- can happen when one grows and splits into two contexts with a lot of overlap
* need active continuous integration; tight team collab
* both teams need to work together as one
context relationship patterns (cont)
there are tradeoffs on both sides of the relationships in terms of:
- app dev and continuous integration
- team communication policies/politics
those tradeoffs are important when analyzing and defining each bounded context relationship
context map
- gives the much needed quick big picture of the entire system - like a "you are here" map
- a graphical layout showing all the bounded contexts in a system
- also describes their relationships with each other e.g. shared kernel, separate ways, etc
context map
- showing the team, members, and roles - tells everyone where they stand and how to co-operate
- a system is a team of apps each playing their positions
- a team is organized/effective when each member knows their role, and their relationship to the other members
next up, "distillation"
but let's revisit a few important fundamentals. cus i wanna touch on some other ideas that are built on the basics. it's hard to understand some of the bigger picture ideas without keeping the basic themes in your head:
domain, model, bounded context
refresher - domain, model, bounded context
domain
model
domain model
bounded context (part of a system)
and whenever you think code/class, replace that with context/concept
now sometimes you're working on an app as part of a system and feel disconnected from purpose of the system or project
now to add on to the "context mapping" description from earlier is the idea of "distillation", and the focus, clarity of the bigger picture that it gives
distillation
the way you get to the nitty gritty
* pulling away concepts/code that blur what the core is
- finding where the heart of the system is
* pinpointing where to focus your energy
- improving your pov of the system design as a whole
i.e. reaching the "core domain"
the next few points are some of the trickiest in ddd but i took a shot at em. stuff like
- core domain
- generic subdomain
- cohesive mechanism
- segregated core
- abstract core
- highlighted core
they're all terms describing the key "parts" of the system around it's "core"
core domain
key part of the system
- the part that makes the system most valuable and unique to the user
- where the secret sauce is kept; the money maker
- key part (contexts/models, apps/teams) of the system
* put the most attention, time, energy here
generic subdomain
support for the core
- the parts (apps/code/teams, contexts/models) outside of the core
- the parts of the system that support the main part
* implements concepts which are important, but not "central" to the domain
cohesive mechanism
tech support for the core
- these parts (mechanisms) help organize the more complex parts of the core
- hold the more techy algorithms - pull those out of the core and stick em here
* strips out that bloat and complexity from the core; makes the core cleaner
segregated core
- when key parts are tangled up tight with the more generic parts, and hard to understand
- key parts have to be carefully untangled from generic parts
* surgically separate the core code from the generic code; moving the core into it's own classes and packages
abstract core
- when a core talks to a bunch of other more generic parts of the codebase, complexity can fog up the core
- there needs to be a layer to simplify
- this is a core that declares an api, [abstract] classes for talking to other parts
declarative style
- a style where you let the code describe "what" should get done. not detailed step by step "how" to do it
- start with the set of building blocks we touched on earlier:
* i.e. intention revealing interfaces, assertions, side effect free functions
domain vision statement
- a 1-pager living document describing the heart of the system (core domain) and the value it brings to the target users
- concise, lean, lightweight; focus the core, blur generic parts
- readily available to dev teams for focus and direction
highlighted core
- add on to the "domain vision statement"; for quickly pointing straight to the core
- 1-5-pager of few key diagrams or docs that flag the core (flagged core)
- makes it painless to know exactly what is (and isn't) the core (distillation document)
with the above understanding, each dev team:
- knows where they stand in the bigger picture;
- can define the core part and other parts of a system
- can describe the different areas of the system and their relationships with more accuracy and confidence
large scale structure
system-wide design rules and patterns
- helps to understand and organize a system as a whole
- keeps design decisions consistent and uniform across system
- helps bring order to the design and development
let's look at 5 forms of large scale structure
1. evolving order
- design that is allowed to evolve - frozen design is worse than no design
- let the caterpillar turn into the butterfly
* devs allow design to mold, and to be molded by, the model #dddfl
- keep rules minimal and let em fit new understandings as they come in
2. system metaphor
- simple story of the system
- in one bar, explain the whole system architecture
- quick and relatable idea of the system design
- provides a consistent view and approach for development
- adds to the shared understanding (model)
3. responsibility layers
- organized by layer
- a large system carries out a set of responsibilities
* layer or organize the structure of the system according to responsibility
- way easier to understand a system's codebase with a layered design
4. knowledge level
- organize the system based on two "levels" or groups of subsystems, applications, classes and domain concepts:
a) those that do the work (operational)
b) those that tell how work is done (knowledge level)
5. pluggable component framework
- organize the system like a hub
- setup system-wide general interfaces or ports on the hub
- let subsystems plug in with their implementations
- helps to bring order to many different subsystems
- like the "abstract core" mentioned earlier
organized code
that caps off the 5 forms of "large scale structure". it's all about orderly codebases, applications, subsystems, systems. this along with the "context map" helps to find footing in a large complex codebase
and with that, we've touched on every main topic in ddd based on the blue book if you're still with me,
congratulations, you are now a certified DDD expert!!!
jk π please don't play yaself and go around claiming to be a ddd guru after reading this. that would be obnoxious
this was a quick ddd bag of delicious bite sized candy coated chocolate covered peanuts. a quick teaser and reference. but there's a lot more ddd goodies to unpack and to dive into
when i started out i was really into software design in general, modeling, object oriented design. i wish i had more awareness of ddd. which is what motivated this thread
so here's to socializing ddd for the masses, and demystifying software design for coders from all walks of life, applications with soul, and better more expressive domain-language-rich system designs in general
now go start coding up some "standalone classes", some "value objects", with "side effect free functions" - and always letting the domain model drive your coding choices
and again, def go read the ddd bible aka the blue book "Domain-Driven Design: Tackling Complexity in the Heart of Software" by Eric Evans
and some parting ddd links for further investigation
- dddcommunity.org
- domainlanguage.com
- github.com/heynickc/awesoβ¦
- ddd.fed.wiki.org
@threadreaderapp plz unroll
Share this Scrolly Tale with your friends.
A Scrolly Tale is a new way to read Twitter threads with a more visually immersive experience.
Discover more beautiful Scrolly Tales like this.
