
Dev Meeting 002: Intro to DDD
My notes from an introduction to Domain-Driven Design (DDD), an approach to software development that models business domains for more effective and maintainable systems.
Key takeaways
- DDD structures code around the business domain, not technical layers.
- Ubiquitous Language ensures developers and domain experts use the same vocabulary.
- Bounded Contexts let a 'Track' mean different things in catalog vs. royalty systems.
- Aggregates group related entities into a single unit for atomic data changes.
Domain-Driven Design (DDD) is an approach to software development where the code is structured around the business domain rather than technical layers. Instead of starting with the database or framework, you start with the problem the business is trying to solve — and let that shape the architecture.
For this dev meeting with my team at the time, Mateusz Kubaszek — consultant, software architect, and data engineering architect — walked us through the core concepts with Python examples.
Core Concepts
A shared vocabulary between developers and domain experts. If the business says "Release," the code has a `Release` class — not a `ContentItem` or `Product`. This eliminates translation errors.
Large systems are split into distinct areas, each with its own model. A "Track" in the catalog context is different from a "Track" in the royalty context — and that's intentional.
Entities have identity (a specific artist, a specific release). Value Objects don't — they're defined by their attributes (an ISRC code, a money amount). This distinction drives how you model data.
A cluster of entities treated as a single unit for data changes. For example, a `Release` aggregate might include its `Tracks`, `Artwork`, and `DealTerms` — all modified together.
A Music Industry Example
Here's what DDD looks like applied to a music distribution platform:
# Value Object — defined by its attributes, no identity
class ISRC:
def __init__(self, code: str):
if not self._validate(code):
raise ValueError(f"Invalid ISRC: {code}")
self.code = code
@staticmethod
def _validate(code: str) -> bool:
return len(code) == 12 and code[:2].isalpha()
def __eq__(self, other):
return isinstance(other, ISRC) and self.code == other.code
# Entity — has identity (id), mutable state
class Track:
def __init__(self, track_id: str, title: str, isrc: ISRC, duration_seconds: int):
self.track_id = track_id
self.title = title
self.isrc = isrc
self.duration_seconds = duration_seconds
# Aggregate Root — controls access to child entities
class Release:
def __init__(self, release_id: str, title: str, artist: str):
self.release_id = release_id
self.title = title
self.artist = artist
self._tracks: list[Track] = []
def add_track(self, track: Track):
if any(t.isrc == track.isrc for t in self._tracks):
raise ValueError(f"Track with ISRC {track.isrc.code} already exists")
self._tracks.append(track)
@property
def total_duration(self) -> int:
return sum(t.duration_seconds for t in self._tracks)
Notice how the code reads like the business domain. A Release has Tracks, a Track has an ISRC. The ubiquitous language is embedded directly in the code.
Why Use DDD?
Developers and business stakeholders speak the same language. No more translating between "business requirements" and "technical specs."
Bounded contexts keep complexity contained. Changes in one area don't cascade through the entire system.
The software evolves with the business. When new rules are added, they map naturally to existing structures.
Bounded contexts map cleanly to microservices. Each context can be deployed, scaled, and maintained independently.
When DDD Makes Sense
DDD is not for every project. It adds upfront complexity that pays off in large, evolving systems with complex business rules. A simple CRUD app doesn't need it.
DDD works best when:
- The business domain is complex and has many rules
- Multiple teams need to work on the same system without stepping on each other
- The system will evolve significantly over time
- Domain experts are available and willing to collaborate with developers
About Mateusz Kubaszek
Mateusz Kubaszek is a consultant, software architect, and data engineering architect with deep experience designing complex technical solutions. His presentation included live Python examples showing DDD patterns in practice.
If you're new to DDD, start with Eric Evans' Domain-Driven Design: Tackling Complexity in the Heart of Software — the book that started it all.
Connecting Your Max for Live Device to a Cloud API
Send song structure data from Ableton Live to a cloud API. Build a complete DAW-to-database pipeline with real-time visualization.
Dev Meeting 003: Web3 Primer
My notes from a Web3 primer. Learn how network interactions are evolving and why companies are adopting Web3 elements in their projects.