Domain-Driven Design (DDD)

VérifiéSûr

Offre une expertise en implémentation des patrons Domain-Driven Design (DDD) comprenant les contextes délimités, les agrégats, les objets-valeurs et les événements métier. Aide à modéliser des domaines métier complexes, à établir un langage omniprésent et à concevoir des cartographies de contexte stratégiques pour aligner l'architecture logicielle sur la logique métier.

Spar Skills Guide Bot
DeveloppementIntermédiaire
16002/06/2026
Claude CodeCursorWindsurfCopilotCodex
#domain-driven-design#bounded-contexts#ubiquitous-language#aggregates#domain-events

Recommandé pour

Notre avis

Aide à implémenter le Domain-Driven Design (DDD) avec des contextes bornés, un langage omniprésent, des agrégats, des dépôts et des événements métier.

Points forts

  • Structuration claire des domaines complexes grâce aux contextes bornés
  • Alignement du code avec le langage métier via le langage omniprésent
  • Séparation nette des responsabilités et encapsulation des règles métier dans les agrégats
  • Gestion des événements métier et évolutivité du modèle

Limites

  • Nécessite une compréhension approfondie du domaine métier
  • Peut introduire une surcharge de complexité pour des domaines simples
  • Risque de paralysie par l'analyse si la modélisation est trop poussée
Quand l'utiliser

Pour des applications avec une logique métier complexe où le domaine est au cœur du système.

Quand l'éviter

Pour des applications CRUD simples ou des prototypes où la rapidité de développement prime sur la modélisation.

Analyse de sécurité

Sûr
Score qualité95/100

The skill contains only DDD conceptual explanations and code examples, with no instructions for executing commands, accessing files, or using any tools. There is no risk of harmful actions.

Aucun point d'attention détecté

Exemples

Design a bounded context
Help me design a bounded context for an e-commerce domain, including aggregates and ubiquitous language.
Implement an aggregate root
Show me how to implement an aggregate root in Python with proper encapsulation and domain events.
Entity vs value object
What is the difference between an entity and a value object in DDD? Give examples.

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