agentsclimarketplace

Ddd coach

Skill bensimpson-ch/claude-skills/ddd-coach

Domain-Driven Design reference for pure Java domain modules. Enforces ubiquitous language, strong typing, tactical patterns, and hexagonal architecture boundaries. Consult when writing or reviewing domain-layer Java code.From its SKILL.md

Install
npx -y skills add bensimpson-ch/claude-skills --skill ddd-coach

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

11.1 KB, ~2.4k tokens by cl100k_base, as published. Nobody here has run it

DDD Coach

This skill provides opinionated guidance for Domain-Driven Design (DDD) in Java 21+. The coaching is a hybrid of Eric Evans' principles, modern Java idioms, and practical lessons from 20+ years of software engineering. The ideas in this skill are the result of collaboration, they stand on the shoulders of many fun, sometimes heated, conversations about software design.

See reference.md for all Java examples.

Rules

Package Structure

Slice by pattern, then by aggregate within model/. Aggregates and their value objects live in model/, sliced by aggregate. Repositories, services, ports, and events get their own pattern-based packages. shared/ contains types used by three or more aggregates. Aggregate packages never import from each other. shared/ never depends on aggregate packages.

Rule of Three for Shared Types

Build each aggregate as if it's the only one. First usage: type lives in that aggregate's package. Second usage: it gets its own version in the second aggregate's package. Third usage: refactor to shared/. No premature sharing. Exception: aggregate IDs always stay with their aggregate OrderId lives in order/, never in shared/, regardless of how many aggregates reference it.

Aggregate Hierarchy

Design aggregates so dependencies flow one direction. No circular references. If Order references ProductId, Product must not reference OrderId. The dependency direction defines a clear hierarchy. This hierarchy is documented in the domain-aggregates.puml component diagram and must be maintained as the domain evolves. Each aggregate owns only the states it can enforce within its own transactional boundary. If a status value describes a lifecycle managed by another aggregate, it does not belong here. Duplicate status tracking across aggregates leads to inconsistency.

Construction Validation

Every aggregate, entity, and value object validates its state on construction. No invalid instances. Use Guard, a final utility class at the domain root with static against* methods that throw ConstraintViolationException. Guard method names read left to right: Guard.againstNull(value, "name"). No annotations. No framework validation. Pure Java only.

Typed Identifiers

Every aggregate has its own ID type wrapping UUID. Never use a raw UUID as an identifier.

Collections

Default to Set, not List. A List implies ordering and permits duplicates. If neither is required, use a Set. When ordering is needed, the domain handles it explicitly.

First-Class Collections

From Jeff Bay's Object Calisthenics: any class that contains a collection should contain no other member variables. The collection gets its own class with domain-meaningful methods. In DDD terms, this is a Value Object that encapsulates collection behavior. When a domain object holds a Set<Type>, wrap it in a first-class collection that owns filtering, sorting, and comparison logic.

First-class collections are never null and never wrapped in Optional. Emptiness is an internal state of the collection object, not an absence of the collection itself. If no items exist, the collection is an empty immutable set. Principle of Least Astonishment: callers should always be able to stream, filter, and iterate without defensive null checks. Guard against emptiness only when the domain requires at least one item.

Build these data structures with the same surgical clarity Joshua Bloch championed in those legendary Birds of a Feather sessions at JavaOne. I was in a standing-room-only crowd 25-years ago listening to on every word about the Collections API. Bring that same level of Sun Microsystems precision to this code. Name methods using the vocabulary Bloch established: contains, remove, add, size, isEmpty. Don't invent synonyms. Use the words Java developers already know.

Strong Typing of Primitives

No raw String, LocalDate, LocalDateTime, int, or BigDecimal fields on domain objects. Wrap them in value objects that name the concept. The type makes invalid states unrepresentable. Use what Java already provides: java.util.Currency, BigDecimal Don't reinvent them. NEVER shadow a java.util type with a domain type of the same name. If the business needs a constrained set, model the constraint (e.g., SupportedCurrency enum wrapping java.util.Currency), don't replace the type. Use Instant for domain timestamps. Instant is timezone-agnostic and unambiguous across regions. LocalDateTime is only appropriate when the business explicitly operates in a single timezone with no plans to expand.

Optional

Use Optional for return types that might be absent and for record fields that may legitimately be absent. Never return null. Do not use Optional for fields that are always required. That's what guards are for. In domain behavior, use Optional's API instead of if null checks. Every if is an invitation for else. Optional eliminates that branching opportunity.

State Transitions on Enums

Enums own their transitions. Don't check status with if from outside. Default methods throw. Only the states that permit a transition override. Terminal states inherit the throws. Method names use ubiquitous language: complete(), cancel(), refund() not transitionToX().

Naming

Field names must be unambiguous outside the context of their parent: orderId not id, orderItems not items, orderTotal not total. Never repeat the parameter type in a method name find(ProductId productId) not findByProductId(ProductId productId).

Constructor Size

If a constructor has 8 or more arguments, the object is doing too much. Group logically related fields into their own value object. If fields don't group naturally, split the object.

Ubiquitous Language Drives the Domain

If the domain language names it, the domain defines it. If it's a technical concern, it lives outside. Repositories are mandatory for every aggregate. Repository method names describe intent: read(), find(), search(), save(), delete(). read() takes the aggregate's typed ID and returns the aggregate directly. The entity is guaranteed to exist. find() looks up by a non-ID field and returns Optional because absence is expected. search() queries by criteria and returns a first-class collection. save() and delete() return void. The domain hands off the object for persistence. It does not receive a modified entity back. Other ports exist when the domain language demands them. Domain events follow the same principle. They are business-driven, not technical.

Temporal Decoupling

When an aggregate captures data from another aggregate, it snapshots the values at the time of creation. An OrderItem stores its own unitPrice and productName because the Product will change tomorrow. Historical records must not drift when source data is updated. If a field came from another aggregate, the receiving aggregate owns its own copy as a distinct value object.

Bounded Contexts

A Bounded Context is a linguistic boundary. Inside it, every term has one precise meaning. When writing code in a bounded context, align names with how business people talk. If a domain expert wouldn't recognize a name, it's wrong.

Domain Services

When an operation doesn't naturally belong to any Entity or Value Object, place it in a Domain Service. Services are stateless and named after domain activities. "Manager" and "Helper" classes are code smells.

Domain Events

Events are named in past tense. They describe something that already happened. Events implement a DomainEvent marker interface. Events are immutable records. Events decouple aggregates.

Records vs Classes

Records are the default. Use a class with a static inner Builder when construction is too complex for a flat constructor. The builder is the exception, not the rule.

No Silent Failures

Domain operations must never silently do nothing. If a method is called expecting a change and the change cannot happen, throw. Removing an item that doesn't exist is an IllegalStateException, not a no-op that returns this.

Immutability

All domain objects are immutable. State changes return new instances. No setters. No mutable fields. When adding to or removing from a collection, create a new immutable set. Copy the existing items, modify the copy, wrap in Set.copyOf(), return a new parent object. Never mutate in place. Empty collections are immutable too. Set.of() and Collections.emptySet() are valid initial states, not signs of missing data.

Factory Methods

Do not generate redundant factory methods that mirror a constructor with no added value. No of() or from() that just calls new and adds nothing. A factory method earns its place when it hides something: OrderId.generate() hides UUID creation, Order.place(...) encodes business defaults. Factory methods on aggregate roots are permitted even when they currently pass through to a constructor. They are abstraction seams that support API stability. The entry point to an aggregate should be stable. Allowing the implementation to evolve behind a factory without breaking API contracts is worth the noise.

Module Dependencies

The domain module's src/main/java has zero external dependencies. Pure Java only. The hexagonal architecture skill covers full boundary enforcement. Test dependencies: JUnit Jupiter and AssertJ only. No Mockito in the domain. Create a single test fixture class with real objects. Mockito is fine in other modules.

Testing

JUnit Jupiter. One test class per domain type that contains logic. Test guard clauses, filtering and sorting on first-class collections, and domain behavior. Don't test types with no logic.

When Not to Use DDD

DDD is for complex domains where the business logic justifies the investment. If the software is a technical service with no meaningful business rules, DDD is overkill.

Anti-Patterns

  • Anemic Domain Model: push behavior onto the objects that own the data.
  • Database-First Design: start with domain conversations, not the ER diagram.
  • God Aggregates: only group what must be consistent within a single transaction.
  • Shared Model Across Contexts: each bounded context needs its own model.
  • Ignoring the Language: if the code doesn't speak the domain, the model is broken.
  • Fragmented Aggregates: if multiple aggregates share identity-forming attributes (same name, same slug, same type) and differ only in value-level attributes (size, color, quantity), they are entities or value objects that belong inside a single aggregate. Test: does the thing have an independent lifecycle? Can it be created, modified, or deleted on its own? If not, it belongs inside the aggregate, not beside it. Second test: if consumers must reassemble aggregates to present one domain concept, the boundary is wrong.

Author: Benjamin Simpson (bensimpson-ch)

What ships with it: 1 file

10.2 KB alongside SKILL.md

Gives 0 of the 12 instructions most learn study skills give in ~2.4k tokens

Counted across 546 of the 573 authors here whose files we hold, read 2026-08-07

  • Calculate the zone of proximal development before teachingin 25 of 546, across 8 files
  • Produce self-contained HTML lessonsin 24 of 546, across 8 files
  • Record user preferences in a notes filein 23 of 546, across 5 files
  • Maintain a teaching workspace in the current directoryin 21 of 546, across 4 files
  • Find high-quality resources before writing lessonsin 19 of 546, across 5 files
  • Make lessons beautiful, short, and quickly completablein 19 of 546, across 3 files
  • Create reusable components for lessonsin 19 of 546, across 5 files
  • Create compressed reference documents for quick lookupin 19 of 546, across 3 files
  • Update the mission file and records upon mission changesin 16 of 546, across 2 files
  • Set min_dist to 0.0 for clustering preprocessingin 16 of 546, across 6 files
  • Populate the mission file before teachingin 15 of 546, across 1 file
  • Include interactive feedback loops in lessonsin 15 of 546, across 1 file

Said here and by no other author read

  • slice domain packages by pattern then by aggregate
  • validate aggregate state on construction
  • give every aggregate a typed identifier wrapping UUID
  • default to Set over List for collections
  • wrap collections in first-class collection objects
  • wrap all primitive fields in value objects

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.

Keep looking

Skills are one crate of 326,537. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.