Return to site

🧩🛠️ TACTICAL DDD: BUILDING CLEAN DOMAIN CODE (WITHOUT THE HYPE)

February 12, 2026

🔸 TLDR

▪️ Tactical DDD helps you write domain code where rules live with data 🧠

▪️ Use Value Objects to prevent invalid states 💎

▪️ Use Aggregates to define consistency boundaries 🔒

▪️ Keep services focused (domain vs application vs infrastructure) 🎛️

▪️ Repositories are for aggregate roots, not every table 📚

🔸 CONTEXT

DDD isn’t a “framework”. Tactical DDD is a set of code-level building blocks to keep your domain model coherent, testable, and hard to misuse 🧠✅ It’s especially useful when business rules get messy (pricing, billing, workflow, compliance, etc.).

🔸 STRUCTURE

▪️ Domain layer: the business rules (your “truth”) 🧾

▪️ Application layer: orchestrates use cases (commands, transactions) 🎛️

▪️ Infrastructure layer: DB, messaging, HTTP, external APIs 🔌

▪️ UI/API layer: controllers, DTOs, serialization 🌐

▪️ Aim: dependencies point inward → domain doesn’t know frameworks 🧅

🔸 OBJECTIVES

▪️ Put rules near the data they protect 🛡️

▪️ Make invalid states unrepresentable 🚫

▪️ Reduce “god services” and anemic models 🧟♂️

▪️ Improve readability: business code that reads like the business 📖

▪️ Enable safe evolution: refactors without breaking everything 🔧

🔸 ENTITIES & VALUE OBJECTS

▪️ Entity = has identity across time (e.g., OrderId) 🆔

▪️ Value Object = defined by its values (e.g., Money, Email) 💎

▪️ Entity pitfalls ⚠️

▪️ Public setters → invariants get bypassed 😬

▪️ Equality by all fields → bugs with persistence / identity 🤯

▪️ “Entity everywhere” → unnecessary complexity 🧨

▪️ Builders & DSL (to enforce valid creation) 🧱

▪️ Use factory methods for common valid states:

Order.open(customerId)

▪️ Use a builder when many optional fields exist

▪️ Use a test DSL to express scenarios clearly:

anOrder().withItem("SKU-1").paid().build() ✅

▪️ Value Object rules 💡

▪️ Immutable ✅

▪️ Validated at creation ✅

▪️ Equality by value ✅

▪️ Examples: Money, Quantity, VatRate, Address, TimeWindow 🧾

🔸 AGGREGATES & AGGREGATE ROOTS

▪️ Aggregate = cluster of objects that must stay consistent together 🔒

▪️ Aggregate Root = the only entry point from the outside 🚪

▪️ Why it matters: invariants are enforced in one place ✅

▪️ Practical rules 🧭

▪️ Keep aggregates small (performance + clarity) 🪶

▪️ Reference other aggregates by ID, not by object graph 🔗

▪️ One transaction = one aggregate consistency boundary 🧾

▪️ Example idea:

Order is root; OrderLine is internal; Product is another aggregate (refer by ProductId) 📦

🔸 SERVICES & THEIR ROLES

Not everything belongs inside entities/value objects — services have legit jobs 👇

▪️ Domain Service 🧠

▪️ Business operation that doesn’t “fit” naturally in one entity

▪️ Stateless, expresses domain language

▪️ Example: pricing policy, risk calculation, matching rules 💰

▪️ Application Service 🎛️

▪️ Orchestrates use cases: load aggregate → call domain behavior → save → publish events

▪️ Talks DTOs / commands, handles transactions

▪️ Should NOT contain business rules (otherwise it becomes a “god service”) 🚫

▪️ Infrastructure Service 🔌

▪️ Technical details: sending emails, HTTP calls, database adapters

▪️ Hidden behind interfaces so the domain stays pure 🧼

🔸 IMPLEMENTING REPOSITORIES

▪️ Repository = collection-like abstraction for aggregates 📚

▪️ Repositories typically exist per aggregate root (not for every entity) 🎯

▪️ Common shape:

▪️ findById(id)

▪️ save(aggregate)

▪️ optionally findBy(...) //when it supports a true use case

▪️ Tips ✅

▪️ Return domain types, not persistence models 🧠

▪️ Avoid leaking ORM concerns into the domain 🚫

▪️ Prefer queries that match business language (e.g., findOpenOrdersFor(customerId)) 🔎

🔸 STRATEGIC DDD VS TACTICAL DDD

▪️ Strategic DDD

= “Where are the boundaries?” 🗺️

▪️ Split the system into Bounded Contexts

▪️ Align teams on a Ubiquitous Language ▪️

Define how contexts integrate (context map) 🔗

▪️ Tactical DDD

= “How do we model it in code?” 🛠️

▪️ Implement the model inside a context with:

▪️ Entities / Value Objects 🆔💎

▪️ Aggregates / Roots 🔒

▪️ Services + Repositories 🧠📚

▪️ Shortcut 🧠

▪️ Strategic: map the domain

▪️ Tactical: code the domain

🔸 TAKEAWAYS

▪️ Start small: create 2–3 Value Objects and enforce validation ✅

▪️ Make aggregates tiny and explicit about invariants 🔒

▪️ If business rules are in application services → you’re drifting to anemic DDD 🚨

▪️ Design APIs that make the “right way” the easiest way 🛠️

▪️ Tactical DDD is about maintainability, not ceremony ✨

#DDD #DomainDrivenDesign #TacticalDDD #SoftwareArchitecture #CleanCode #Backend #Java #SpringBoot #Microservices #DesignPatterns #SoftwareEngineering #Refactoring

Go further with Java certification:

Java👇

Spring👇

SpringBook👇

JavaBook👇