You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在DDD架构中,应由哪个类负责为实体生成ID?

How to Handle Aggregate Root ID Generation Without Breaking Domain Layer Dependency Rules

Hey there, let's work through this together—you're absolutely right to guard that Domain layer dependency boundary (keeping it free from external components like concrete Repositories), and Vernon's advice from the DDD Red Book doesn't have to clash with that. Here's a clean way to reconcile both requirements:

1. Define the ID Generation Contract in the Domain Layer

First, add a method for generating aggregate root IDs directly to your Domain-layer Repository interface. This keeps the contract (what needs to be done) in the Domain, while leaving the "how" to the Infrastructure layer.

For example, in your project.Domain code:

// project.Domain/Repositories/IMyAggregateRootRepository.cs
public interface IMyAggregateRootRepository
{
    MyAggregateRootId GenerateAggregateRootId();
    // Your other standard Repository methods (Add, GetById, etc.) go here
}

This way, the Domain only depends on its own abstraction—no external dependencies creep in.

2. Implement the ID Generation Logic in Infrastructure

In your project.Infrastructure layer, where you implement the Repository interface, you can plug in the actual ID generation logic. This is where you'd handle external dependencies like UUID libraries, database auto-increment queries, or distributed ID services—all things that don't belong in the Domain.

Example implementation:

// project.Infrastructure/Repositories/MyAggregateRootRepository.cs
public class MyAggregateRootRepository : IMyAggregateRootRepository
{
    public MyAggregateRootId GenerateAggregateRootId()
    {
        // Example: Generate a UUID (could also pull from DB, use snowflake ID, etc.)
        return new MyAggregateRootId(Guid.NewGuid().ToString());
    }

    // Implement other Repository methods here (e.g., Add to database)
}

3. Use the Domain Interface to Create Aggregates

When you need to create a new aggregate root in the Domain, use the injected Repository interface to fetch the ID. This keeps all Domain logic isolated from external concerns.

You could do this in a static factory method on the aggregate root itself:

// project.Domain/Aggregates/MyAggregateRoot.cs
public class MyAggregateRoot
{
    public MyAggregateRootId Id { get; }
    // Other aggregate properties and behavior here

    // Private constructor to enforce creation via factory method
    private MyAggregateRoot(MyAggregateRootId id, /* other required parameters */)
    {
        Id = id;
        // Initialize aggregate state and enforce invariants here
    }

    public static MyAggregateRoot Create(IMyAggregateRootRepository repo, /* other parameters */)
    {
        var aggregateId = repo.GenerateAggregateRootId();
        return new MyAggregateRoot(aggregateId, /* other parameters */);
    }
}

4. Why This Fits Vernon's Guidance

Vernon recommends putting ID generation in the Repository because ID strategies are often tied to persistence:

  • If you use database auto-increment IDs, the Repository needs to interact with the database to get the next valid ID.
  • If you use distributed IDs, the Repository might need to call an external ID service.

By keeping the contract in the Domain and the implementation in Infrastructure, you honor both Vernon's advice and your rule of keeping the Domain layer free from external dependencies. The Domain only cares that it can get a valid ID—how that ID is generated is none of its business.

内容的提问来源于stack exchange,提问作者Paweł Hemperek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:50:04