gpt4 book ai didi

OnionArch2.0-基于DDD的洋葱架构改进版开源

转载 作者:我是一只小鸟 更新时间:2023-02-07 22:32:03 24 4
gpt4 key购买 nike

大家好, 去年我发布了一篇  OnionArch - 采用DDD+CQRS+.Net 7.0实现的洋葱架构  。 很多程序员都比较感兴趣,给我要源代码。 这次我把OnionArch进行了升级,改进了一些特性,并放出源代码, iamxiaozhuang/OnionArch2 (github.com)    欢迎大家研究使用.

1、自动生成和发布领域事件

我在OninArch1.0实现了对删除的实体自动生成和发布领域事件,并通过订阅这个领域事件,将删除的实体数据备份至回收站表中,以备审计和数据恢复.

本次我改进了这个特性,对实体数据的新增,修改和删除都会自动生成和发布领域事件。我认为尽量不要通过修改代码来新增和发布领域事件,这会导致新增的业务功能也需要修改代码而不是新增代码来实现,不符合对修改关闭和对扩展开放的设计原则。应该对实体数据的任何变动都自动发布领域事件,然后在事件Handler中筛选需要的领域事件并进行处理.

我基于这个特性实现了按配置自动审计记录和和按配置自动发布集成事件功能.

按配置自动审计功能

我们可以通过配置的方式来实现那些实体,那种修改类型,那个字段需要审计。请看如下配置:

                "EntityChangedAuditLogsConfig"
                
                  : [
    {
      
                
                "EntityFullName": "OnionArch.Domain.ProductInventory.ProductInventory"
                
                  ,
      
                
                "ChangeType": "Added"
                
                  
    },
    {
      
                
                "EntityFullName": "OnionArch.Domain.ProductInventory.ProductInventory"
                
                  ,
      
                
                "ChangeType": "Modified"
                
                  ,
      
                
                "Properties": "InventoryAmount"
                
                  
    },
    {
      
                
                "EntityFullName": "OnionArch.Domain.ProductInventory.ProductInventory"
                
                  ,
      
                
                "ChangeType": "Deleted"
                
                  
    }
  ],
                
              

可以按照实体的全名,修改类型(新增,修改,删除),甚至是实体的修改字段来配置是否需要进行数据审计,如过需要审计则会自动保存审计日志到审计表,审计表包含实体的原值和当前值,修改人和修改时间等.

按配置自动发布集成事件功能

 我们可以通过配置来实现该微服务的那些领域事件需要转为集成事件发布出去,供其它的微服务订阅使用。这样我们在微服务中新增集成事件订阅的时候就不需要修改源微服务的代码,只需要在源微服务中增加配置即可.

我们需要配置Dapr的发布订阅名称,事件Topic和这个Topic的发布条件,例如,在产品仓库实体的库存数量被修改后发布Topic为“ProductInventoryAmountChanged”集成事件.

                "EntityChangedIntegrationEventConfig"
                
                  : {
    
                
                "PubsubName": "pubsub"
                
                  ,
    
                
                "Topics"
                
                  : [
      {
        
                
                "TopicName": "ProductInventoryAmountChanged"
                
                  ,
        
                
                "EntityFullName": "OnionArch.Domain.ProductInventory.ProductInventory"
                
                  ,
        
                
                "ChangeType": "Modified"
                
                  ,
        
                
                "Properties": "InventoryAmount"
                
                  
      }
    ]
  },
                
              

然后就可通过Dapr在其它的微服务中订阅并处理该集成事件,通过Dapr发布集成事件的代码请查看源代码.

已发布的集成事件也会自动保存至集成事件记录表中,以备对该事件进行后续执行跟踪和重发.

2、自动生成Minimal WebApi接口

该特性我在  根据MediatR的Contract Messages自动生成Minimal WebApi接口  中做过介绍。因为OninArch通过MediatR实现了CQRS和其它AOP功能,例如业务实体验证,异常处理、工作单元等特性.

本次将OninArch1.0的GRPC接口替换成了自动生成的WebAPI接口。并对自动生成WebAPI接口做了改进,可以指定生成的WebAPI的Http方法,地址、介绍和详细说明。自动对接口按命名空间分类,将Get方法参数自动映射到Query参数等.

                 [MediatorWebAPIConfig(HttpMethod = HttpMethodToGenerate.Post, HttpUrl = 
                
                  "
                
                
                  /productinventory
                
                
                  "
                
                , Summary = 
                
                  "
                
                
                  创建产品库存
                
                
                  "
                
                , Description = 
                
                  "
                
                
                  创建产品库存 Description
                
                
                  "
                
                
                  )]
    
                
                
                  public
                
                
                  class
                
                 CreateProductInventory : ICommand<Unit>
                
                  
    {
        
                
                
                  public
                
                
                  string
                
                 ProductCode { 
                
                  get
                
                ; 
                
                  set
                
                
                  ; }
        
                
                
                  public
                
                
                  int
                
                 InventoryAmount { 
                
                  get
                
                ; 
                
                  set
                
                
                  ; }
    }
    [MediatorWebAPIConfig(HttpMethod 
                
                = HttpMethodToGenerate.Patch, HttpUrl = 
                
                  "
                
                
                  /productinventory/increase
                
                
                  "
                
                , Summary = 
                
                  "
                
                
                  增加产品库存
                
                
                  "
                
                
                  )]
    
                
                
                  public
                
                
                  class
                
                 IncreaseProductInventory : ICommand<Unit>
                
                  
    {
        
                
                
                  public
                
                 Guid Id { 
                
                  get
                
                ; 
                
                  set
                
                
                  ; }
        
                
                
                  public
                
                
                  int
                
                 Amount { 
                
                  get
                
                ; 
                
                  set
                
                
                  ; }
    }
                
              

生成的WebAPI:

  。

 3、对充血模型的支持

我在OninArch1.0中并没有刻意强调充血模型,本次按照我对充血模型的理解改进了仓储代码,即,仓储服务只实现实体的Add,Remove和Query,不实现实体的Create和Modify。实体的创建和修改必须放入实体中实现。也就是说,实体字段的set都是私有的,只能在实体内部对实体的字段进行修改,以保证将业务逻辑封装到实体中,并提高系统的稳定性和业务逻辑重用性.

                
                  public
                
                
                  static
                
                 ProductInventory Create<TModel>
                
                  (TModel model)
        {
            
                
                
                  //
                
                
                  var entity = new ProductInventory();
                
                
                  var
                
                 entity = model.Adapt<ProductInventory>
                
                  ();
            
                
                
                  return
                
                
                   entity;
        }

        
                
                
                  public
                
                 ProductInventory Update<TModel>
                
                  (ProductInventory entity, TModel model)
        {
            model.Adapt(entity);
            
                
                
                  return
                
                
                   entity;
        }

        
                
                
                  public
                
                
                  int
                
                 InventoryAmount { 
                
                  get
                
                ; 
                
                  private
                
                
                  set
                
                
                  ; }

        
                
                
                  public
                
                
                  void
                
                 IncreaseInventory(
                
                  int
                
                
                   amount)
        {
            
                
                
                  this
                
                .InventoryAmount +=
                
                   amount;
        }
                
              

仓储接口新增了Edit方法,以获取实体对象,再调用实体对象内部方法进行实体数据的修改.

仓库接口的Query方法不直接返回实体对象,而是直接返回Model对象(Dto、VO),提高数据库查询性能(通过Mapster的ProjectToType方法实现).

仓储服务代码如下:

                  
                    using
                  
                  
                     MediatR;

                  
                  
                    using
                  
                  
                     OnionArch.Domain.Common.Entities;

                  
                  
                    using
                  
                  
                     OnionArch.Domain.Common.Paged;

                  
                  
                    using
                  
                  
                     System.Linq.Expressions;


                  
                  
                    namespace
                  
                  
                     OnionArch.Domain.Common.Repositories
{
    
                  
                  
                    public
                  
                  
                    class
                  
                   RepositoryService<TEntity> 
                  
                    where
                  
                  
                     TEntity : BaseEntity
    {
        
                  
                  
                    private
                  
                  
                    readonly
                  
                  
                     IMediator _mediator;

        
                  
                  
                    public
                  
                  
                     RepositoryService(IMediator mediator)
        {
            _mediator 
                  
                  =
                  
                     mediator;
        }

        
                  
                  
                    ///
                  
                  
                    <summary>
                  
                  
                    ///
                  
                  
                     创建单个实体
        
                  
                  
                    ///
                  
                  
                    </summary>
                  
                  
                    ///
                  
                  
                    <param name="entity"></param>
                  
                  
                    ///
                  
                  
                    <returns></returns>
                  
                  
                    public
                  
                  
                    async
                  
                   Task<TEntity>
                  
                     Add(TEntity entity)
        {
            
                  
                  
                    return
                  
                  
                    await
                  
                   _mediator.Send(
                  
                    new
                  
                   AddEntityRequest<TEntity>
                  
                    (entity));
        }
        
                  
                  
                    ///
                  
                  
                    <summary>
                  
                  
                    ///
                  
                  
                     创建多个实体
        
                  
                  
                    ///
                  
                  
                    </summary>
                  
                  
                    ///
                  
                  
                    <param name="entities"></param>
                  
                  
                    ///
                  
                  
                    <returns></returns>
                  
                  
                    public
                  
                  
                    async
                  
                   Task Add(
                  
                    params
                  
                  
                     TEntity[] entities)
        {
            
                  
                  
                    await
                  
                   _mediator.Send(
                  
                    new
                  
                   AddEntitiesRequest<TEntity>
                  
                    (entities));
        }

        
                  
                  
                    ///
                  
                  
                    <summary>
                  
                  
                    ///
                  
                  
                     删除单个实体
        
                  
                  
                    ///
                  
                  
                    </summary>
                  
                  
                    ///
                  
                  
                    <param name="Id"></param>
                  
                  
                    ///
                  
                  
                    <returns></returns>
                  
                  
                    public
                  
                  
                    async
                  
                   Task<TEntity>
                  
                     Remove(Guid Id)
        {
            
                  
                  
                    return
                  
                  
                    await
                  
                   _mediator.Send(
                  
                    new
                  
                   RemoveEntityRequest<TEntity>
                  
                    (Id));
        }
        
                  
                  
                    ///
                  
                  
                    <summary>
                  
                  
                    ///
                  
                  
                     删除多个实体
        
                  
                  
                    ///
                  
                  
                    </summary>
                  
                  
                    ///
                  
                  
                    <param name="whereLambda"></param>
                  
                  
                    ///
                  
                  
                    <returns></returns>
                  
                  
                    public
                  
                  
                    async
                  
                   Task<
                  
                    int
                  
                  > Remove(Expression<Func<TEntity, 
                  
                    bool
                  
                  >>
                  
                     whereLambda)
        {
            
                  
                  
                    return
                  
                  
                    await
                  
                   _mediator.Send(
                  
                    new
                  
                   RemoveEntitiesRequest<TEntity>
                  
                    (whereLambda));
        }

         
                  
                  
                    ///
                  
                  
                    <summary>
                  
                  
                    ///
                  
                  
                     获取单个实体以更新实体字段
        
                  
                  
                    ///
                  
                  
                    </summary>
                  
                  
                    ///
                  
                  
                    <param name="Id"></param>
                  
                  
                    ///
                  
                  
                    <returns></returns>
                  
                  
                    public
                  
                  
                    async
                  
                   Task<TEntity>
                  
                     Edit(Guid Id)
        {
            
                  
                  
                    return
                  
                  
                    await
                  
                   _mediator.Send(
                  
                    new
                  
                   EditEntityRequest<TEntity>
                  
                    (Id));
        }

        
                  
                  
                    public
                  
                  
                    async
                  
                   Task<IQueryable<TEntity>> Edit(Expression<Func<TEntity, 
                  
                    bool
                  
                  >>
                  
                     whereLambda)
        {
            
                  
                  
                    return
                  
                  
                    await
                  
                   _mediator.Send(
                  
                    new
                  
                   EditEntitiesRequest<TEntity>
                  
                    (whereLambda));
        }


        
                  
                  
                    ///
                  
                  
                    <summary>
                  
                  
                    ///
                  
                  
                     查询单个实体(不支持更新实体)
        
                  
                  
                    ///
                  
                  
                    </summary>
                  
                  
                    ///
                  
                  
                    <param name="Id"></param>
                  
                  
                    ///
                  
                  
                    <returns></returns>
                  
                  
                    public
                  
                  
                    async
                  
                   Task<TModel> Query<TModel>
                  
                    (Guid Id)
        {
            
                  
                  
                    return
                  
                  
                    await
                  
                   _mediator.Send(
                  
                    new
                  
                   QueryEntityRequest<TEntity,TModel>
                  
                    (Id));
        }
        
                  
                  
                    ///
                  
                  
                    <summary>
                  
                  
                    ///
                  
                  
                     查询多个实体
        
                  
                  
                    ///
                  
                  
                    </summary>
                  
                  
                    public
                  
                  
                    async
                  
                   Task<IQueryable<TModel>> Query<TModel>(Expression<Func<TEntity, 
                  
                    bool
                  
                  >>
                  
                     whereLambda)
        {
            
                  
                  
                    return
                  
                  
                    await
                  
                   _mediator.Send(
                  
                    new
                  
                   QueryEntitiesRequest<TEntity,TModel>
                  
                    (whereLambda));
        }
        
                  
                  
                    ///
                  
                  
                    <summary>
                  
                  
                    ///
                  
                  
                     分页查询多个实体
        
                  
                  
                    ///
                  
                  
                    </summary>
                  
                  
                    ///
                  
                  
                    <typeparam name="TOrder"></typeparam>
                  
                  
                    ///
                  
                  
                    <param name="whereLambda"></param>
                  
                  
                    ///
                  
                  
                    <param name="pageOption"></param>
                  
                  
                    ///
                  
                  
                    <param name="orderbyLambda"></param>
                  
                  
                    ///
                  
                  
                    <param name="isAsc"></param>
                  
                  
                    ///
                  
                  
                    <returns></returns>
                  
                  
                    public
                  
                  
                    async
                  
                   Task<PagedResult<TModel>> Query<TOrder,TModel>(Expression<Func<TEntity, 
                  
                    bool
                  
                  >> whereLambda, PagedOption pagedOption, Expression<Func<TEntity, TOrder>> orderbyLambda, 
                  
                    bool
                  
                   isAsc = 
                  
                    true
                  
                  
                    )
        {
            
                  
                  
                    return
                  
                  
                    await
                  
                   _mediator.Send(
                  
                    new
                  
                   QueryPagedEntitiesRequest<TEntity, TOrder,TModel>
                  
                    (whereLambda, pagedOption, orderbyLambda, isAsc));
        }

        
                  
                  
                    ///
                  
                  
                    <summary>
                  
                  
                    ///
                  
                  
                     判断是否有存在
        
                  
                  
                    ///
                  
                  
                    </summary>
                  
                  
                    ///
                  
                  
                    <param name="whereLambda"></param>
                  
                  
                    ///
                  
                  
                    <returns></returns>
                  
                  
                    public
                  
                  
                    async
                  
                   Task<
                  
                    bool
                  
                  > Any(Expression<Func<TEntity, 
                  
                    bool
                  
                  >>
                  
                     whereLambda)
        {
            
                  
                  
                    return
                  
                  
                    await
                  
                   _mediator.Send(
                  
                    new
                  
                   AnyEntitiesRequest<TEntity>
                  
                    (whereLambda));
        }
    }
}
                  
                
View Code

4、对采用MediatR代替接口的探索

如上仓库服务代码,我并没有创建仓库接口并实现,而是完全基于MediatR直接实现仓库服务。这个我在 MediatRPC - 基于MediatR和Quic通讯实现的RPC框架,比GRPC更简洁更低耦合,开源发布第一版  的MediatR编程思想中做过介绍,本次是实现这个编程思想,即不通过接口和依赖注入,而是通过MediatR来实现控制反转。如果大家不喜欢这种方式也可以修改回接口的方式.

鉴于篇幅所限,不能一一说明本次升级的所有改动,请大家下载代码自行研究,下面又到了找工作时间(是的,我还在找工作).

5、找工作

▪ 博主有15年以上的软件技术经验(曾担任架构师和技术 Leader),擅长云原生、微服务和领域驱动软件架构设计,.Net Core  开发。 ▪ 博主有15年以上的项目交付经验(曾担任项目经理和产品经理),专注于敏捷(Scrum )项目管理,业务分析和产品设计。 ▪ 博主熟练配置和使用 Microsoft Azure 和Microsoft 365云(曾担任微软顾问)。 ▪ 博主为人诚恳,工作认真负责,态度积极乐观.

我家在广州,也可以去深圳工作。做架构师、产品经理、项目经理都可以。有工作机会推荐的朋友可以加我微信 15920128707,微信名字叫Jerry.

  。

最后此篇关于OnionArch2.0-基于DDD的洋葱架构改进版开源的文章就讲到这里了,如果你想了解更多关于OnionArch2.0-基于DDD的洋葱架构改进版开源的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com