OnionArch - 采用DDD+CQRS+.Net 7.0实现的洋葱架构

博主最近失业在家,找工作之余,看了一些关于洋葱(整洁)架构的资料和项目,有感而发,自己动手写了个洋葱架构解决方案,起名叫OnionArch 。基于最新的.Net 7.0 RC1, 数据库采用PostgreSQL, 目前实现了包括多租户在内的12个特性 。
该架构解决方案主要参考了NorthwindTraders,sample-dotnet-core-cqrs-api 项目, B站上杨中科的课程代码以及博主的一些项目经验 。
洋葱架构的示意图如下:

OnionArch - 采用DDD+CQRS+.Net 7.0实现的洋葱架构

文章插图
一、OnionArch 解决方案说明解决方案截图如下:
OnionArch - 采用DDD+CQRS+.Net 7.0实现的洋葱架构

文章插图
可以看到,该解决方案轻量化实现了洋葱架构,每个层都只用一个项目表示 。建议将该解决方案作为单个微服务使用,不建议在领域层包含太多的领域根 。
源代码分为四个项目:
1. OnionArch.Domain- 核心领域层,类库项目,其主要职责实现每个领域内的业务逻辑 。设计每个领域的实体(Entity),值对象、领域事件和领域服务,在领域服务中封装业务逻辑,为应用层服务 。-   领域层也包含数据库仓储接口,缓存接口、工作单元接口、基础实体、基础领域跟实体、数据分页实体的定义,以及自定义异常等 。
2. OnionArch.Infrastructure- 基础架构层,类库项目,其主要职责是实现领域层定义的各种接口适配器(Adapter) 。例如数据库仓储接口、工作单元接口和缓存接口,以及领域层需要的其它系统集成接口 。-  基础架构层也包含Entity Framework基础DbConext、ORM配置的定义和数据迁移记录 。
3. OnionArch.Application- 应用(业务用例)层,类库项目,其主要职责是通过调用领域层服务实现业务用例 。一个业务用例通过调用一个或多个领域层服务实现 。不建议在本层实现业务逻辑 。-   应用(业务用例)层也包含业务用例实体(Model)、Model和Entity的映射关系定义,业务实基础命令接口和查询接口的定义(CQRS),包含公共MediatR管道(AOP)处理和公共Handler的处理逻辑 。
4. OnionArch.GrpcService- 界面(API)层,GRPC接口项目,用于实现GRPC接口 。通过MediatR特定业务用例实体(Model)消息来调用应用层的业务用例 。- 界面(API)层也包含对领域层接口的实现,例如通过HttpContext获取当前租户和账号登录信息 。
二、OnionArch已实现特性说明1.支持多租户(通过租户字段)基于Entity Framework实体过滤器和实现对租户数据的查询过滤
protected override void OnModelCreating(ModelBuilder modelBuilder){//加载配置modelBuilder.ApplyConfigurationsFromAssembly(typeof(TDbContext).Assembly);//为每个继承BaseEntity实体增加租户过滤器// Set BaseEntity rules to all loaded entity typesforeach (var entityType in GetBaseEntityTypes(modelBuilder)){var method = SetGlobalQueryMethod.MakeGenericMethod(entityType);method.Invoke(this, new object[] { modelBuilder, entityType });}}在BaseDbContext文件的SaveChanges之前对实体租户字段赋值
//为每个继承BaseEntity的实体的Id主键和TenantId赋值var baseEntities = ChangeTracker.Entries<BaseEntity>();foreach (var entry in baseEntities){switch (entry.State){case EntityState.Added:if (entry.Entity.Id == Guid.Empty)entry.Entity.Id = Guid.NewGuid();if (entry.Entity.TenantId == Guid.Empty)entry.Entity.TenantId = _currentTenantService.TenantId;break;}}多租户支持全部在底层实现,包括租户字段的索引配置等 。开发人员不用关心多租户部分的处理逻辑,只关注业务领域逻辑也业务用例逻辑即可 。

经验总结扩展阅读