<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      OnionArch 2.0 - 基于DDD的洋蔥架構改進版開源

      大家好,去年我發布了一篇 OnionArch - 采用DDD+CQRS+.Net 7.0實現的洋蔥架構 很多程序員都比較感興趣,給我要源代碼。這次我把OnionArch進行了升級,改進了一些特性,并放出源代碼,iamxiaozhuang/OnionArch2 (github.com)   歡迎大家研究使用。

      一、自動生成和發布領域事件

      我在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發布集成事件的代碼請查看源代碼。

      已發布的集成事件也會自動保存至集成事件記錄表中,以備對該事件進行后續執行跟蹤和重發。

      二、自動生成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:

       

       三、對充血模型的支持

      我在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

      四、對采用MediatR代替接口的探索

      如上倉庫服務代碼,我并沒有創建倉庫接口并實現,而是完全基于MediatR直接實現倉庫服務。這個我在MediatRPC - 基于MediatR和Quic通訊實現的RPC框架,比GRPC更簡潔更低耦合,開源發布第一版 的MediatR編程思想中做過介紹,本次是實現這個編程思想,即不通過接口和依賴注入,而是通過MediatR來實現控制反轉。如果大家不喜歡這種方式也可以修改回接口的方式。

      鑒于篇幅所限,不能一一說明本次升級的所有改動,請大家下載代碼自行研究,下面又到了找工作時間(是的,我還在找工作)。

      五、找工作

      ? 博主有15年以上的軟件技術經驗(曾擔任架構師和技術 Leader),擅長云原生、微服務和領域驅動軟件架構設計,.Net Core  開發。
      ? 博主有15年以上的項目交付經驗(曾擔任項目經理和產品經理),專注于敏捷(Scrum )項目管理,業務分析和產品設計。
      ? 博主熟練配置和使用 Microsoft Azure 和Microsoft 365云(曾擔任微軟顧問)。
      ? 博主為人誠懇,工作認真負責,態度積極樂觀。

      我家在廣州,也可以去深圳工作。做架構師、產品經理、項目經理都可以。有工作機會推薦的朋友可以加我微信 15920128707,微信名字叫Jerry。

       

      posted on 2023-02-07 16:37  小莊  閱讀(1448)  評論(2)    收藏  舉報

      主站蜘蛛池模板: 久久久久蜜桃精品成人片公司| 天天干天天色综合网| 国产人妻精品午夜福利免费| 日韩精品一区二区三区中文| 精品偷自拍另类精品在线| 午夜免费福利小电影| 亚洲国产精品色一区二区| 英德市| 素人视频亚洲十一十二区| 亚洲av成人免费在线| 日韩高清亚洲日韩精品一区二区| 亚洲成aⅴ人在线电影| av 日韩 人妻 黑人 综合 无码| 一个人免费观看WWW在线视频| 欧洲女人牲交性开放视频| 日本一区二区久久人妻高清| 国产一区二区日韩在线| 亚洲av日韩在线资源| 猫咪AV成人永久网站在线观看 | 亚洲av无码片在线播放| 国产精品久久无码不卡黑寡妇| 欧美乱码精品一区二区三区| 尤物国产精品福利在线网| 亚洲中文字幕在线二页| 国产清纯在线一区二区| 亚洲精品无码在线观看| 人妻另类 专区 欧美 制服| 亚洲码欧洲码一二三四五| 万荣县| 国产一区日韩二区欧美三区| 精品亚洲国产成人| 日韩av爽爽爽久久久久久| 久久这里都是精品二| 免费观看全黄做爰大片国产| 国产午夜三级一区二区三| 高清自拍亚洲精品二区| 国产精品一在线观看| 男人天堂亚洲天堂女人天堂| 狠狠躁天天躁中文字幕无码| 天堂在线精品亚洲综合网| 国产亚洲精品VA片在线播放|