思考一种好的架构(七)

 

先来看看商品业务服务库

 

 

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

依赖:

 

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

必选:

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






 

 

 

 

 

 

 

posted @   AnAng  阅读(151)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示