• 思考一种好的架构(七)


    先来看看商品业务服务库

    有控制器、有实体、有自己的服务、有配置

    依赖:

     这也是所有业务服务库的大体依赖

    必选:

    中介者服务(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服务是在中介者服务中的,中介者服务会在后续说到






     
     
  • 相关阅读:
    Elasticsearch 缓存总结
    ElasticSearch-集群
    HTTP协议详解
    HTTPS总结
    ElasticSearch--Document
    正排索引和倒排索引
    线上OOM排查步骤总结
    线程池-四种拒绝策略总结
    netty篇-练手
    netty篇-UDP广播
  • 原文地址:https://www.cnblogs.com/AnAng/p/12611146.html
Copyright © 2020-2023  润新知