思考一種好的架構(七)

 

先來看看商品業務服務庫

 

 

有控制器、有實體、有自己的服務、有配置

依賴:

 

 這也是所有業務服務庫的大體依賴

必選:

中介者服務(Mediator)

數據庫訪問(ORM.Chloe)

路由(Route)

可選:

對象映射服務(AutoMapper)

掃描服務(ReferenceScan)

庫存業務服務庫(Stock)

 

控制器:

 [ApiController]
    [APIRouth("Product")]
    public class ProductController : Controller
    {
        IProduct productServer;
        public ProductController(IProduct productServer) {
            this.productServer = productServer;
        }
        /// <summary>
        /// 獲取全部商品信息
        /// </summary>
        /// <param name="maxNumber">庫存最大數量</param>
        /// <returns></returns>
        [HttpGet]
        public IActionResult GetAll(int? maxNumber)
        {
            var result =productServer.GetAll(maxNumber);
            return Json(result);
        }
        /// <summary>
        /// 獲得單個商品信息
        /// </summary>
        /// <param name="ID">商品ID</param>
        /// <returns></returns>
        [HttpGet]
        public IActionResult Get(int ID=0)
        {
            var result = productServer.GetSingle(ID);
            return Json(result);
        }

    }

其中 

APIRouth特性頭是編寫的一個特性

 

 

 /// <summary>
    /// API路由
    /// </summary>
    public class APIRouth : Microsoft.AspNetCore.Mvc.RouteAttribute
    {
        /// <summary>
        /// 設計一個API的路由
        /// </summary>
        /// <param name="area">區域名稱</param>
        public APIRouth(string area) : base($"api/{area}/[controller]/[action]")
        {

        }
    }

這是爲了達到統一API路徑的目的

 

控制器很簡單

只執行調用IProduct 服務的功能,在後續開發中,他也是比較貧血的一層

可以預料的是,它的作用僅僅只是爲了提供一個訪問入口且寫明註釋,並調用了服務

至於表單驗證,響應模型之類的我們將會在中間件中完成,可以會有困難,但還是儘量中間件中完成,如果不行那麼將會仍在過濾器或者父類控制器完成,儘量保證不會在業務中出現這種重複性代碼

 

 

實體:

 [Table(Name = "Product")]
    public class ProductEntity
    {
        [ColumnAttribute(IsPrimaryKey = true)]
        [AutoIncrement]
        public int ID { get; set; }

        public string Title { get; set; }

        public string Describe { get; set; }
    }

 業務處理服務:

  public class ProductServer : IProduct
    {
        IDbContext DC;
        IStock stock;
        IMapper mapper;
        public ProductServer(IDbContext DC, IStock stock,IMapper mapper) {
            this.DC = DC;
            this.stock = stock;
            this.mapper = mapper;
        }
        public List<ProductInfoDTO> GetAll(int? maxNumber)
        {
            var result = DC.Query<ProductEntity>().RightJoin(stock.StockNumberQuery(null, maxNumber), (c, z) =>
            c.ID.Equals(z.ProductID))
                .Select((x, z) => new ProductInfoDTO()
                {
                    Describe = x.Describe,
                    ID = x.ID,
                    StockNumber = z.Number,
                    Title = x.Title

                });
            return result.ToList();
        }

        public ProductInfoDTO GetSingle(int ID)
        {
           return DC.Query<ProductEntity>().InnerJoin(stock.GetSingle(ID), (c, z) => c.ID.Equals(z.ProductID)).Select((x, z) => new ProductInfoDTO()
            {
                Describe = x.Describe,
                ID = x.ID,
                StockNumber = z.Number,
                Title = x.Title

            }).FirstOrDefault(x => x.ID.Equals(ID)); ;
        }
    }
DC   數據庫上下文
stock 庫存服務
mapper (AutoMapper) 對象映射服務


這裏有一個跨服務的聯合查詢
在數據庫中 商品和庫存是兩個關聯的表
代碼中通過
RightJoin
InnerJoin
來進行鏈表查詢

  public interface IStock
    {
        /// <summary>
        /// 庫存查詢
        /// </summary>
        /// <param name="minNumber">最小庫存</param>
        /// <param name="maxNumber">最大庫存</param>
        /// <returns></returns>
        IQuery<StockDTO> StockNumberQuery(int? minNumber, int? maxNumber);

        /// <summary>
        /// 庫存查詢
        /// </summary>
        /// <param name="product">商品ID</param>
        /// <returns></returns>
        IQuery<StockDTO> GetSingle(int productID);

      /// <summary>
      /// 消減庫存
      /// </summary>
      /// <param name="productID">商品ID</param>
      /// <param name="number">數量</param>
      /// <returns></returns>
        bool ReducedInventory(int productID,int number);
    }
跨庫查詢也是一個非常大的問題,這裏我們還只是一個同數據庫不同服務的跨服務查詢,後續的跨服務跨庫查詢纔是真正難以解決的問題

庫存服務返回了一個IQuery查詢對象,方便Chloe進行鏈表操作
最後在Product服務中去執行關聯和返回查詢實體,
這裏沒有用到mapper,因爲Chloe的Select已經足夠使用,沒必要再去添一行代碼去使用mapper

啓動執行:
  public class StartExecute : BaseStartExecute
    {
        public override void Execute()
        {
            MapperPost.CreateMap(x => x.CreateMap<ProductInfoDTO, ProductEntity>());
            MapperPost.CreateMap(x => x.CreateMap<ProductEntity, ProductInfoDTO>().ForMember(dest => dest.StockNumber, opt => opt.Ignore()));
        }
    }
Mapper需要在啓動的時候也就是ConfigureServices函數中就進行調用,這會有個問題,大家爭搶ConfigureServices函數執行順序,變得非常難以管理,所以
StartExecute出現了,它會在所有服務調用ConfigureServices前調用,並逐個執行Execute函數,這樣一來,就不用關注ConfigureServices函數的執行順序啦
具體代碼後續在說


啓動類:
 public static class Startup
    {
        public static void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IProduct, ProductServer>();

        }

        public static void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            
        }
    }

 

在這裏註冊了IProduct服務,
IProduct服務是在中介者服務中的,中介者服務會在後續說到






 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章