本系列专栏基于杨中科老师的《ASP.NET Core技术内幕与项目 实战》,本人记录梳理的学习笔记,有部分的增补和省略。更全面系统的讲解,请看杨老师的视频课:【.NET教程,.Net Core视频教程,杨中科主讲】。
一、微服务
1. 核心概念
微服务是一种架构设计模式,核心是将传统单体应用拆分为多个小型、自治、独立部署的服务单元,每个服务专注于单一业务领域,服务间通过轻量级通信协议(如HTTP/REST、gRPC、消息队列)实现交互,适配ASP.NET Core的跨平台、轻量、高性能特性,尤其适合中小型服务的快速搭建与迭代。
2. 优势
-
耦合性极低,开发维护更高效:服务拆分后,各模块边界清晰,单个服务的代码量大幅缩减,开发人员可专注单一业务,修改、调试、迭代互不干扰,解决了单体应用牵一发而动全身的痛点,团队协作效率显著提升。
-
技术栈灵活适配:不同于单体应用的技术绑定,微服务支持多技术栈共存,核心业务可用ASP.NET Core搭建,数据处理服务可选用Python/Java,无需局限于单一技术体系,贴合不同业务场景的技术需求。
-
独立扩容,资源利用率高:针对高并发、高负载的核心服务(如订单支付、用户登录),可单独进行水平扩容,非核心服务保持低配运行,避免整体扩容造成的资源浪费,适配流量波动大的业务场景。
-
服务隔离,故障影响可控:单个服务出现异常、宕机时,不会波及其他服务,通过熔断、降级、限流等容错机制,可最大程度保障系统整体可用性,大幅降低全局故障风险。
-
部署周期短,迭代速度快:单个服务体积小,编译、打包、部署流程简化,支持持续集成/持续部署(CI/CD),新功能上线、bug修复的效率远高于单体应用,适配敏捷开发需求。
3. 缺点
-
运维复杂度陡增:服务数量增多后,服务监控、日志归集、配置管理、服务注册发现等运维工作量大幅提升,需搭建完善的运维体系(如Docker容器化、K8s编排、ELK日志栈),对运维团队能力要求极高。
-
运行效率有所损耗:服务间通过网络通信,相比单体应用的内存调用,存在网络延迟、序列化反序列化开销,复杂业务的多服务调用链路会进一步降低响应速度,需优化通信方式与调用链路。
-
技术门槛高,分布式难题棘手:需解决分布式事务、数据一致性、服务链路追踪、幂等性等问题,尤其是事务最终一致性,无法沿用单体应用的本地事务方案,对开发人员的分布式技术储备要求严苛。
-
拆分难度大,易过度拆分:若拆分不合理,会出现服务粒度过细、调用链路冗长、数据冗余等问题,反而增加开发与维护成本,这也是DDD方法论需要解决的核心问题。
二、DDD:领域驱动设计
Domain-Driven Design(领域驱动设计,简称DDD),是一套聚焦业务本质的软件设计方法论,并非单纯的编码规范,而是贯穿需求分析、架构设计、代码开发、测试运维全生命周期的指导思想。在ASP.NET Core微服务架构中,DDD能有效解决服务拆分混乱、业务逻辑散落在代码各处、技术与业务脱节的问题,让开发、产品、测试、业务人员基于统一的业务认知开展工作,摒弃"重技术、轻业务"的开发误区,始终站在用户与业务视角设计系统。
1. 领域与领域模型
(1)领域与子领域
领域(Domain):简单来说,就是组织的核心业务范围,是企业开展经营、系统需要解决的业务问题集合,比如电商公司的领域是"电商交易"、手机公司的领域是"手机研发、生产、销售"。实际业务中,复杂领域会拆分为多个子领域,各司其职支撑整体业务运转。
结合手机公司案例,子领域可分为三类,直接决定企业研发资源投入优先级:
-
核心域 :企业的核心竞争力所在,解决项目核心业务问题,与组织业务深度绑定。手机公司的核心域为手机研发与核心功能设计,需投入顶级研发资源,是系统设计的重中之重。
-
支撑域 :服务于核心域,解决非核心业务问题,具备企业专属特性,无行业通用性。手机公司的支撑域为供应链管理、售后维修、品牌营销,保障核心业务落地,研发投入次之。
-
通用域 :全行业通用的基础业务,无企业专属特性,无需投入核心研发资源。手机公司的通用域为用户登录认证、日志管理、权限控制、文件存储,可选用成熟组件或开源方案快速实现。
(2)领域模型
领域模型是对领域内业务对象、业务规则、业务行为的抽象提炼,是DDD的核心载体。ASP.NET Core项目落地DDD,必须先构建领域模型,而非先设计数据库表结构、编写CRUD代码,这是DDD与传统数据驱动开发的核心区别。
通过领域模型,整个团队可使用纯业务语言描述系统,而非技术术语(如"数据表""字段""接口"),即便非技术人员也能理解系统设计逻辑,彻底消除业务与技术的沟通壁垒。领域模型不是一成不变的,需随着业务迭代持续优化,贴合实际业务变化。
2. 通用语言与界限上下文
(1)通用语言
通用语言是DDD的沟通基石,指团队内部约定的、含义唯一、无歧义、精准的业务语言,覆盖需求文档、设计图纸、代码注释、类名方法名、日常沟通全场景。比如电商场景中,"订单"统一指代用户下单后的交易单据,杜绝"单据""交易单""结算单"等模糊称呼,避免因术语不统一导致的开发偏差。
(2)界限上下文
通用语言并非全局通用,必须依托**界限上下文(Bounded Context)**存在,简单理解就是通用语言的有效作用边界。同一个术语在不同界限上下文中,含义完全不同:比如"用户"在"会员上下文"中是拥有会员等级、积分的主体,在"权限上下文"中是拥有账号、密码的访问主体,二者虽同名但业务属性不同。
在微服务架构中,界限上下文是微服务拆分的核心依据,一个界限上下文通常对应一个微服务,既能保证服务内业务内聚,又能避免跨领域的业务混淆,解决微服务拆分无标准的难题。
3. 实体与值对象
DDD将领域内的对象分为实体和值对象,二者的核心区别在于是否具备唯一标识符,这也是ASP.NET Core中设计领域类的核心准则,与数据库主键设计思路存在本质差异。
(1)实体
实体拥有唯一且不可变更的标识符,即便对象的属性、状态不断变化,也能通过标识符精准定位。比如电商订单,订单ID(标识符)唯一不变,订单状态、收货地址、商品明细可随时修改,但始终是同一个订单;数据库中通常用主键(自增ID、GUID)实现标识符,但标识符的核心是"业务唯一性",而非单纯的数据库主键。
实体的核心价值是承载业务状态,跟踪状态变化,是领域模型中核心业务对象的载体。
(2)值对象
值对象无唯一标识符,自身不具备独立存在的意义,必须依附于实体存在,由多个属性组合描述实体的某一特征。比如订单的"收货地址",包含省、市、区、详细地址、手机号,无唯一标识,依附于订单实体;地址修改后,本质是替换为新的值对象,而非修改原有对象。
值对象的核心特性是不可变性,设计时应设为只读,避免随意修改引发业务混乱,能有效简化领域对象设计,提升代码可读性。
4. 聚合与聚合根
(1)聚合
DDD设计的核心目标之一是高内聚、低耦合:将业务关联紧密的实体、值对象打包为聚合,实现内部高度协作;弱化关联的对象隔离在不同聚合,减少跨聚合依赖,避免业务逻辑杂乱、数据耦合严重的问题。
(2)聚合根
每个聚合有且仅有一个聚合根,它是特殊的实体,既是聚合内的核心对象,也是聚合的"管理者"与"对外入口"。外部对象只能通过聚合根访问聚合内部成员,禁止直接引用聚合内的普通实体/值对象,所有对聚合内数据的增删改查操作,都必须经由聚合根触发。
以电商订单场景为例:订单(聚合根)、订单明细、收货地址、支付信息组成订单聚合,外部操作订单相关业务,只能调用订单聚合根的方法,无法直接修改订单明细,既能保证聚合内数据一致性,又能简化外部调用逻辑。
5. 领域服务与应用服务
DDD严格区分业务逻辑层级,将逻辑拆分到领域服务与应用服务中,避免业务代码散落在控制器、数据层,实现逻辑解耦,适配ASP.NET Core的分层架构(Controller、Service、Domain、Repository)。
(1)对象职责
聚合内的实体、值对象仅负责自身状态管理、对象初始化、基础属性校验,不承载复杂业务逻辑,保证领域对象的纯粹性。
(2)领域服务
领域服务承载聚合内的核心业务逻辑,处理单一聚合内的业务规则、业务行为,纯业务聚焦,不涉及任何外部系统交互(如数据库操作、缓存读写、第三方接口调用),完全贴合领域规则,是业务核心能力的封装。
(3)应用服务
应用服务属于上层协调服务,承载跨聚合协作、聚合与外部系统交互的逻辑,核心作用是协调多个领域服务、调用外部接口、操作数据持久化,完成完整业务用例。
(4)DDD典型用例处理流程
-
数据准备阶段:应用服务接收前端请求,通过仓储层获取业务所需的领域对象、基础数据,完成参数校验与数据预处理;
-
业务执行阶段:调用领域服务,执行核心业务逻辑,修改领域对象状态、生成业务结果,全程不接触外部系统;
-
结果落地阶段:应用服务将领域对象的状态变更,通过仓储层同步至数据库、缓存等外部系统,完成数据持久化。
注意:领域服务并非必需项。简单CRUD业务无复杂业务逻辑,仅需基础数据操作,可直接由应用服务+仓储层实现,无需强行引入领域服务,避免过度设计、增加代码冗余。
6. 领域事件与集成事件
DDD通过事件机制实现解耦通信,分为领域事件与集成事件,适配ASP.NET Core微服务的进程内、跨服务通信场景,可依托MediatR、CAP等开源组件快速落地。
-
领域事件 :作用于同一微服务/界限上下文内部,用于聚合间的状态同步、业务触发,采用进程内通信机制(如MediatR中介者模式),无需依赖中间件,响应速度快。例:订单创建后,触发库存扣减的领域事件。
-
集成事件 :作用于跨微服务/界限上下文,用于分布式业务的数据同步,依托事件总线(EventBus)+消息队列(RabbitMQ、Kafka)实现,保证事件可靠投递,解决跨服务事务一致性问题。例:订单支付完成后,触发物流服务发货的集成事件。
7. 贫血模型与充血模型
这是DDD与传统开发模式的核心代码差异,直接决定领域模型的合理性,也是ASP.NET Core落地DDD的关键编码准则。
-
贫血模型:类仅包含属性/成员变量,无任何业务方法,仅作为数据载体,业务逻辑散落在Service层。这是传统三层架构的常见模式,看似简单,实则违背DDD理念,导致业务逻辑碎片化、难以维护。
-
充血模型:类既包含属性/成员变量,又封装自身的业务方法、业务规则,将"数据"与"行为"绑定,贴合真实业务逻辑,是DDD推崇的设计模式。比如订单实体,不仅有订单ID、状态等属性,还包含"创建订单""取消订单""支付订单"等业务方法,保证领域对象的完整性。
总结
- DDD不是单纯的技术框架,而是业务驱动的设计思想,与ASP.NET Core微服务天然契合。
- 落地核心是"先懂业务,再做设计",通过领域划分、模型抽象、逻辑分层,打造易维护、易扩展、贴合业务的分布式系统。