Dev Meeting 002: Intro to DDD

An introduction to Domain-Driven Design (DDD), an approach to software development that models business domains for more effective and maintainable systems.
Dev Meeting 002: Intro to DDD

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, Mateusz Kubaszek — consultant, software architect, and data engineering architect — walked us through the core concepts with Python examples.

Core Concepts

Ubiquitous Language
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.
Bounded Contexts
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 & Value Objects
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.
Aggregates
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?

Better communication
Developers and business stakeholders speak the same language. No more translating between "business requirements" and "technical specs."
Maintainability
Bounded contexts keep complexity contained. Changes in one area don't cascade through the entire system.
Business alignment
The software evolves with the business. When new rules are added, they map naturally to existing structures.
Scalability
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.

Need Help with This?

Building something similar or facing technical challenges? We've been there.

Let's talk — no sales pitch, just honest engineering advice.

Share this article

Mariusz Smenżyk
Mariusz Smenżyk

Technical Partner

Technical partner at MusicTech Lab with 15+ years in software development. Builder, problem solver, blues guitarist, long-distance swimmer, and cyclist.

Newsletter

Get music tech insights, case studies, and industry news delivered to your inbox.