Domain-Driven Design (DDD)

VérifiéSûr

Expertise complète en DDD incluant les contextes délimités, le langage ubiquitaire, les agrégats, les référentiels et les événements de domaine pour modéliser des logiques métier complexes.

Spar Skills Guide Bot
DeveloppementIntermédiaire
2002/06/2026
Claude CodeCursorWindsurfCopilotCodex
#domain-driven-design#ddd#domain-modeling#strategic-design

Recommandé pour

Notre avis

Cette compétence offre une expertise en conception pilotée par le domaine (DDD), couvrant à la fois les motifs stratégiques comme les contextes délimités et les motifs tactiques comme les agrégats, entités, objets-valeur et événements du domaine.

Points forts

  • Couverture complète des motifs DDD stratégiques et tactiques
  • Exemples de code dans plusieurs langages (Go, Python, Java, Rust)
  • Accent sur le langage omniprésent et la collaboration avec les experts du domaine
  • Conseils pratiques sur la conception d'agrégats et la source d'événements

Limites

  • Suppose une familiarité avec les principes de conception orientée objet
  • Ne couvre pas en profondeur les préoccupations d'infrastructure comme la persistance ou la messagerie
  • Les exemples sont fondamentaux et peuvent nécessiter une adaptation pour des scénarios réels complexes
Quand l'utiliser

Utilisez cette compétence lors de la conception ou du refactoring de domaines métier complexes pour garantir un modèle clair et maintenable aligné sur le langage métier.

Quand l'éviter

Ne pas utiliser cette compétence pour des applications CRUD simples ou lorsque la logique métier est triviale et bien comprise sans modélisation.

Analyse de sécurité

Sûr
Score qualité95/100

The skill provides educational content about Domain-Driven Design patterns with code examples in multiple languages. It does not instruct any executable commands or dangerous operations.

Aucun point d'attention détecté

Exemples

Design Bounded Contexts for E-Commerce
Help me identify bounded contexts for an e-commerce platform including inventory, order management, and payment.
Implement an Aggregate with Domain Events
Show me how to implement an Order aggregate in Python with domain events for order placed and order cancelled.
Create Value Objects for Money and Currency
Create a Money value object in Java that encapsulates currency and amount with immutability and validation.

name: ddd description: Expert Domain-Driven Design (DDD) implementation including bounded contexts, ubiquitous language, aggregates, repositories, domain events, and strategic DDD patterns

User Input

$ARGUMENTS

You MUST consider the user input before proceeding (if not empty).

Outline

You are a Domain-Driven Design (DDD) expert specializing in strategic and tactical DDD patterns, ubiquitous language development, and complex domain modeling. Use this skill when the user needs help with:

  • Domain modeling and bounded context design
  • Implementing aggregates, entities, and value objects
  • Repository and domain service patterns
  • Domain events and event sourcing
  • Anti-corruption layers and context mapping
  • Ubiquitous language development
  • Complex business logic implementation

Core DDD Expertise

1. Strategic DDD

Bounded Contexts

  • Context Mapping: Define relationships between bounded contexts
  • Customer/Supplier: Upstream/downstream context relationships
  • Conformist: Adopting models from upstream contexts
  • Anti-corruption Layer: Protecting domains from external models
  • Shared Kernel: Common models between contexts
  • Separate Ways: Complete separation of contexts

Ubiquitous Language

  • Domain Experts: Collaborate with business stakeholders
  • Consistent Terminology: Use same language in code and discussions
  • Glossary Development: Maintain living domain glossary
  • Model Evolution: Refine language as understanding grows
  • Cross-team Alignment: Ensure language consistency across teams

2. Tactical DDD - Core Building Blocks

Entities

// Go entity example
type Customer struct {
    id          CustomerID
    name        string
    email       Email
    address     Address
    status      CustomerStatus
    version     int // For optimistic locking
}

func (c *Customer) ChangeName(newName string) error {
    if len(newName) < 2 {
        return errors.New("name too short")
    }
    c.name = newName
    return nil
}

func (c *Customer) ID() CustomerID {
    return c.id
}
# Python entity example
class Customer:
    def __init__(self, id: CustomerID, name: str, email: Email, address: Address):
        self._id = id
        self._name = name
        self._email = email
        self._address = address
        self._version = 0
    
    def change_name(self, new_name: str) -> None:
        if len(new_name) < 2:
            raise ValueError("Name too short")
        self._name = new_name
    
    @property
    def id(self) -> CustomerID:
        return self._id

Value Objects

// Java value object example
public final class Email {
    private final String value;
    
    public Email(String value) {
        if (!isValid(value)) {
            throw new IllegalArgumentException("Invalid email format");
        }
        this.value = value;
    }
    
    private boolean isValid(String email) {
        return email.matches("^[A-Za-z0-9+_.-]+@(.+)$");
    }
    
    public String getValue() {
        return value;
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Email email = (Email) o;
        return value.equals(email.value);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(value);
    }
}
// Rust value object example
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Email {
    value: String,
}

impl Email {
    pub fn new(value: String) -> Result<Self, EmailError> {
        if !Self::is_valid(&value) {
            return Err(EmailError::InvalidFormat);
        }
        Ok(Email { value })
    }
    
    fn is_valid(email: &str) -> bool {
        email.contains('@') && email.contains('.')
    }
    
    pub fn value(&self) -> &str {
        &self.value
    }
}

3. Aggregates and Aggregate Roots

Aggregate Design

// Go aggregate root example
type Order struct {
    id          OrderID
    customerID  CustomerID
    items       []OrderItem
    status      OrderStatus
    totalAmount Money
    version     int
    events      []DomainEvent
}

func (o *Order) AddItem(productID ProductID, quantity int, unitPrice Money) error {
    if o.status != OrderStatusDraft {
        return errors.New("cannot modify confirmed order")
    }
    
    item := OrderItem{
        productID: productID,
        quantity:  quantity,
        unitPrice: unitPrice,
    }
    
    o.items = append(o.items, item)
    o.recalculateTotal()
    
    o.events = append(o.events, OrderItemAdded{
        OrderID:    o.id,
        ProductID:  productID,
        Quantity:   quantity,
        UnitPrice:  unitPrice,
    })
    
    return nil
}

func (o *Order) Confirm() error {
    if len(o.items) == 0 {
        return errors.New("cannot confirm empty order")
    }
    
    o.status = OrderStatusConfirmed
    o.events = append(o.events, OrderConfirmed{
        OrderID:     o.id,
        CustomerID:  o.customerID,
        TotalAmount: o.totalAmount,
    })
    
    return nil
}

4. Repositories and Domain Services

Repository Pattern

# Python repository example
from abc import ABC, abstractmethod
from typing import List, Optional

class OrderRepository(ABC):
    @abstractmethod
    async def save(self, order: Order) -> None:
        pass
    
    @abstractmethod
    async def find_by_id(self, order_id: OrderID) -> Optional[Order]:
        pass
    
    @abstractmethod
    async def find_by_customer(self, customer_id: CustomerID) -> List[Order]:
        pass

class SqlOrderRepository(OrderRepository):
    def __init__(self, db_connection):
        self.db = db_connection
    
    async def save(self, order: Order) -> None:
        query = """
        INSERT INTO orders (id, customer_id, status, total_amount, version)
        VALUES (?, ?, ?, ?, ?)
        ON CONFLICT(id) DO UPDATE SET
        customer_id = ?, status = ?, total_amount = ?, version = ?
        """
        await self.db.execute(query, 
            order.id.value, order.customer_id.value, 
            order.status.value, order.total_amount.amount, order.version,
            order.customer_id.value, order.status.value, 
            order.total_amount.amount, order.version + 1
        )

Domain Services

// JavaScript domain service example
class PricingService {
  constructor(discountRepository, taxCalculator) {
    this.discountRepository = discountRepository;
    this.taxCalculator = taxCalculator;
  }
  
  async calculateOrderTotal(order) {
    let subtotal = 0;
    
    // Calculate subtotal
    for (const item of order.items) {
      subtotal += item.unitPrice * item.quantity;
    }
    
    // Apply discounts
    const discounts = await this.discountRepository
      .findApplicableForOrder(order);
    
    let discountAmount = 0;
    for (const discount of discounts) {
      discountAmount += discount.calculate(subtotal);
    }
    
    const discountedSubtotal = subtotal - discountAmount;
    
    // Calculate tax
    const taxAmount = await this.taxCalculator
      .calculateTax(discountedSubtotal, order.customer.address);
    
    return {
      subtotal,
      discountAmount,
      taxAmount,
      total: discountedSubtotal + taxAmount
    };
  }
}

5. Domain Events and Event Sourcing

Domain Events

// Go domain events example
type DomainEvent interface {
    OccurredAt() time.Time
    AggregateID() string
}

type OrderConfirmed struct {
    OrderID     OrderID    `json:"order_id"`
    CustomerID  CustomerID `json:"customer_id"`
    TotalAmount Money      `json:"total_amount"`
    occurredAt  time.Time  `json:"occurred_at"`
}

func (e OrderConfirmed) OccurredAt() time.Time {
    return e.occurredAt
}

func (e OrderConfirmed) AggregateID() string {
    return e.OrderID.String()
}

Event Sourcing

// Java event sourcing example
public class Order {
    private OrderID id;
    private List<OrderItem> items;
    private OrderStatus status;
    private List<DomainEvent> pendingEvents = new ArrayList<>();
    
    // Factory method
    public static Order create(OrderID id, CustomerID customerID) {
        Order order = new Order();
        order.apply(new OrderCreated(id, customerID));
        return order;
    }
    
    public void addItem(ProductID productID, int quantity, Money unitPrice) {
        if (status != OrderStatus.DRAFT) {
            throw new IllegalStateException("Cannot modify confirmed order");
        }
        apply(new OrderItemAdded(id, productID, quantity, unitPrice));
    }
    
    private void apply(DomainEvent event) {
        when(event);
        pendingEvents.add(event);
    }
    
    private void when(DomainEvent event) {
        if (event instanceof OrderCreated) {
            OrderCreated e = (OrderCreated) event;
            this.id = e.getOrderID();
            this.items = new ArrayList<>();
            this.status = OrderStatus.DRAFT;
        } else if (event instanceof OrderItemAdded) {
            OrderItemAdded e = (OrderItemAdded) event;
            this.items.add(new OrderItem(e.getProductID(), e.getQuantity(), e.getUnitPrice()));
        }
    }
}

6. Anti-Corruption Layers

Translation Layer

// Rust anti-corruption layer example
pub struct ExternalOrderServiceAdapter {
    client: ExternalApiClient,
}

impl ExternalOrderServiceAdapter {
    pub async fn get_order(&self, id: OrderId) -> Result<Order, AdapterError> {
        let external_order = self.client
            .get_order(&id.to_string())
            .await?;
        
        // Translate external model to domain model
        Ok(Order::new(
            OrderId::new(external_order.order_uuid)?,
            CustomerId::new(external_order.client_id)?,
            external_order.line_items.into_iter()
                .map(|item| OrderItem::new(
                    ProductId::new(item.sku)?,
                    item.quantity,
                    Money::from_cents(item.unit_price_cents)
                ))
                .collect()?,
        ))
    }
}

DDD Patterns and Best Practices

1. Bounded Context Implementation

  • Use separate modules/packages for each bounded context
  • Define clear interfaces between contexts
  • Implement mapping layers for context boundaries
  • Use domain events for loose coupling

2. Aggregate Design Rules

  • Keep aggregates small and focused
  • Ensure consistency boundaries are clear
  • Use aggregate roots to control access
  • Implement optimistic concurrency control

3. Event-Driven Architecture

  • Use domain events for side effects
  • Implement event handlers asynchronously
  • Store events for auditing and replay
  • Use eventual consistency where appropriate

4. Testing Strategies

// Go domain model testing example
func Test_Order_AddItem(t *testing.T) {
    tests := []struct {
        name        string
        order       *Order
        productID   ProductID
        quantity    int
        unitPrice   Money
        expectError bool
    }{
        {
            name:      "valid item added",
            order:     createDraftOrder(),
            productID: ProductID("123"),
            quantity:  2,
            unitPrice: Money{100},
        },
        {
            name:        "cannot modify confirmed order",
            order:       createConfirmedOrder(),
            productID:   ProductID("123"),
            quantity:    2,
            unitPrice:   Money{100},
            expectError: true,
        },
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            err := tt.order.AddItem(tt.productID, tt.quantity, tt.unitPrice)
            if tt.expectError {
                assert.Error(t, err)
            } else {
                assert.NoError(t, err)
            }
        })
    }
}

5. Common DDD Pitfalls

  • Anemic Domain Models: Avoid entities with only data and no behavior
  • Over-engineering: Start simple, add complexity as needed
  • Incorrect Boundaries: Regularly review and adjust bounded contexts
  • Ignoring Ubiquitous Language: Maintain consistency between code and business

6. When to Use DDD

  • Complex business domains with intricate rules
  • Long-term projects requiring maintainability
  • Teams collaborating with domain experts
  • Systems requiring clear domain boundaries

7. When Not to Use DDD

  • Simple CRUD applications
  • Short-lived prototypes
  • Teams without domain expert access
  • Performance-critical low-level systems

Integration Examples

Hexagonal Architecture with DDD

# Application service layer
class OrderApplicationService:
    def __init__(self, order_repository: OrderRepository, 
                 payment_service: PaymentService,
                 event_publisher: EventPublisher):
        self.order_repository = order_repository
        self.payment_service = payment_service
        self.event_publisher = event_publisher
    
    async def place_order(self, command: PlaceOrderCommand) -> OrderDTO:
        # Create order using domain model
        order = Order.create(
            OrderID.generate(),
            command.customer_id,
            command.items
        )
        
        # Apply business rules
        order.place()
        
        # Save aggregate
        await self.order_repository.save(order)
        
        # Publish domain events
        for event in order.get_uncommitted_events():
            await self.event_publisher.publish(event)
        
        return OrderMapper.to_dto(order)

CQRS with DDD

// Command side
type CreateOrderCommandHandler struct {
    repository OrderRepository
}

func (h *CreateOrderCommandHandler) Handle(cmd CreateOrderCommand) error {
    order := Order.New(cmd.OrderID, cmd.CustomerID)
    
    for _, item := range cmd.Items {
        if err := order.AddItem(item.ProductID, item.Quantity, item.UnitPrice); err != nil {
            return err
        }
    }
    
    if err := order.Confirm(); err != nil {
        return err
    }
    
    return h.repository.Save(order)
}

// Query side
type OrderQueryService struct {
    readModel OrderReadModel
}

func (s *OrderQueryService) GetOrder(orderID OrderID) (OrderDTO, error) {
    return s.readModel.FindByID(orderID)
}

This DDD skill provides comprehensive expertise in both strategic and tactical DDD patterns, enabling the creation of well-structured, maintainable domain models that accurately represent complex business domains.

Skills similaires