领域驱动设计(DDD)是一种围绕业务领域建模的软件设计方法,在C#企业级项目中应用广泛,其核心是通过合理的分层划分明确各模块职责,降低代码耦合度。合理的DDD分层架构能够让C#项目结构清晰,后续迭代和维护更加高效。

DDD分层架构的核心层级
标准的DDD分层架构在C#项目中通常分为四层,各层的职责和依赖关系有明确规范:
- 领域层(Domain Layer):整个项目的核心,包含业务实体、值对象、领域服务、领域事件等纯业务相关的逻辑,不依赖任何外部框架或其他层级。
- 应用层(Application Layer):负责协调领域层的对象完成具体的业务用例,不包含核心业务逻辑,仅做流程编排,依赖领域层。
- 基础设施层(Infrastructure Layer):提供技术实现支持,比如数据库访问、缓存操作、第三方接口调用等,依赖领域层定义的接口。
- 表现层(Presentation Layer):负责和用户交互,比如Web API、前端页面等,依赖应用层获取业务数据,调用应用层接口完成操作。
C#项目的具体组织结构
在C#解决方案中,我们可以为每个层级创建独立的类库项目,以下是典型的项目命名和结构:
- 领域层项目:
Demo.Domain,内部可以按业务子域拆分文件夹,比如User、Order,每个子域下包含实体、值对象、领域服务、仓储接口等文件。 - 应用层项目:
Demo.Application,内部按业务用例拆分服务类,每个服务类对应一组相关的业务操作,同时包含DTO(数据传输对象)定义。 - 基础设施层项目:
Demo.Infrastructure,内部按技术类型拆分文件夹,比如Repository实现仓储接口、DbContext实现数据库上下文、Cache实现缓存操作等。 - 表现层项目:
Demo.WebApi,作为启动项目,包含控制器、配置、中间件等Web相关逻辑。
项目引用规则
各层之间的引用必须遵循单向依赖原则,避免循环引用:
| 项目 | 可引用的项目 |
|---|---|
| Demo.Domain | 无,不引用其他层级项目 |
| Demo.Application | Demo.Domain |
| Demo.Infrastructure | Demo.Domain |
| Demo.WebApi | Demo.Application、Demo.Infrastructure |
核心代码示例
领域层示例
领域层定义核心业务对象和仓储接口,不包含任何技术实现:
// Demo.Domain/User/User.cs 用户实体
namespace Demo.Domain.User
{
public class User
{
public Guid Id { get; private set; }
public string UserName { get; private set; }
public string Email { get; private set; }
// 私有构造函数,仅允许通过工厂方法或仓储创建
private User() { }
public User(Guid id, string userName, string email)
{
Id = id;
UserName = userName;
// 领域内的业务校验
if (string.IsNullOrEmpty(userName))
throw new ArgumentException("用户名不能为空");
if (!email.Contains("@"))
throw new ArgumentException("邮箱格式不正确");
UserName = userName;
Email = email;
}
// 领域行为:修改邮箱
public void ChangeEmail(string newEmail)
{
if (!newEmail.Contains("@"))
throw new ArgumentException("新邮箱格式不正确");
Email = newEmail;
}
}
}
// Demo.Domain/User/IUserRepository.cs 用户仓储接口
namespace Demo.Domain.User
{
public interface IUserRepository
{
Task<User> GetByIdAsync(Guid id);
Task<User> GetByUserNameAsync(string userName);
Task AddAsync(User user);
}
}
应用层示例
应用层编排领域对象完成业务用例,不包含核心业务逻辑:
// Demo.Application/User/UserAppService.cs 用户应用服务
namespace Demo.Application.User
{
public class UserAppService
{
private readonly IUserRepository _userRepository;
public UserAppService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
// 创建用户用例
public async Task<Guid> CreateUserAsync(string userName, string email)
{
// 校验用户是否已存在
var existUser = await _userRepository.GetByUserNameAsync(userName);
if (existUser != null)
throw new InvalidOperationException("用户名已存在");
// 创建领域实体
var user = new User(Guid.NewGuid(), userName, email);
// 调用仓储保存
await _userRepository.AddAsync(user);
return user.Id;
}
}
}
基础设施层示例
基础设施层实现领域层定义的接口,完成技术落地:
// Demo.Infrastructure/Repository/UserRepository.cs 用户仓储实现
namespace Demo.Infrastructure.Repository
{
public class UserRepository : IUserRepository
{
private readonly AppDbContext _dbContext;
public UserRepository(AppDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<User> GetByIdAsync(Guid id)
{
return await _dbContext.Users.FirstOrDefaultAsync(u => u.Id == id);
}
public async Task<User> GetByUserNameAsync(string userName)
{
return await _dbContext.Users.FirstOrDefaultAsync(u => u.UserName == userName);
}
public async Task AddAsync(User user)
{
await _dbContext.Users.AddAsync(user);
await _dbContext.SaveChangesAsync();
}
}
}
表现层示例
表现层调用应用层接口,处理用户请求:
// Demo.WebApi/Controllers/UserController.cs 用户控制器
namespace Demo.WebApi.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
private readonly UserAppService _userAppService;
public UserController(UserAppService userAppService)
{
_userAppService = userAppService;
}
[HttpPost]
public async Task<IActionResult> CreateUser([FromBody] CreateUserDto dto)
{
try
{
var userId = await _userAppService.CreateUserAsync(dto.UserName, dto.Email);
return Ok(new { UserId = userId });
}
catch (Exception ex)
{
return BadRequest(new { Message = ex.Message });
}
}
}
public class CreateUserDto
{
public string UserName { get; set; }
public string Email { get; set; }
}
}
落地注意事项
在实际使用DDD分层架构组织C#项目时,需要注意以下几点:
- 领域层不要引入任何技术框架依赖,保持纯业务逻辑的独立性,方便后续替换技术实现。
- 应用层不要直接操作数据库或调用第三方接口,所有技术相关的操作都通过领域层定义的接口交给基础设施层实现。
- 仓储接口定义在领域层,实现放在基础设施层,遵循依赖倒置原则,降低领域层对技术实现的依赖。
- 如果项目规模较小,可以适当简化层级,比如将应用层和表现层合并,但不建议破坏核心的依赖规则。
- 领域事件可以在领域层定义,应用层或基础设施层订阅处理,实现业务解耦。
遵循以上实践组织C#项目结构,能够让项目具备良好的可维护性和扩展性,后续业务迭代时只需在对应层级调整代码,不会影响其他模块的稳定性。