应用分层看起来很简单,但每个程序员都有自己的一套方法,哪怕是初学者,所以实施起来并非易事
最早接触的分层架构应该是最熟悉的 MVC(Model - View - Controller)架构,其将应用分成了模型、视图和控制层,可以说引导了绝大多数开发者
而现在的应用(包括框架)中非常多架构设计都使用此模式
之后又演变出了 MVP(Model - View - Prosenter)和 MVVM(Model - View - ViewModel)
这些可以说都是随着技术的不断发展,为了应对不同场景所演化出来的模型
而微服务的每个架构都可以再细分领域模型
经典的领域模型架构包括:Domain、Service 和 Reposotories
核心实体(Entity)和值对象(Value Object)应该在 Domain 层,定义的领域服务(Domain Service)在 Service 层,而针对实体和值对象的存储和查询逻辑都应该在 Reposotories 层
值得注意的是,不要把 Entity 的属性和行为分离到 Domain 和 Service 两层中去实现,即所谓的贫血模型,史诗证明这样的实现方式会造成很大的维护问题
基于这种设计,工程的结构可以构造为:
- MicroService-Sample/src/
domain
gateways
interface
repositories
services
当然,在微服务的架构中,每个微服务不必严格遵守这样的规定,切忌死搬硬套,最重要的是理解业务
在不同的业务场合,架构的设计可以适当地调整,毕竟适合的架构一定要具有灵活性
分层原则
应用分层采用文件夹方式的优点是可大可小、简单易用、统一规范,可以包括 5 个项目,也可以包括 50 个项目,以满足所有业务应用的多种不同场景
在开发过程中,需要遵循分层架构的约束,禁止跨层次的调用
以用户为中心,以目标为导向
上层(业务逻辑层)需要什么,下层(数据访问层)就提供什么,而不是下层(业务逻辑层)有什么,就向上层(业务逻辑层)提供什么
Entity 是数据表对象,不是数据访问层对象
DTO 是网络传输对象,不是表现层对象
BO 是内存计算逻辑对象,不是业务逻辑层对象,不是只能给业务逻辑层使用
若仅限定在本层访问,则导致单个应用内大量没有价值的对象转换
以用户为中心类设计实体类,可以减少重复对象和无用转换
下行时表现层是 Input,业务逻辑层是 Process,数据访问层是 Output
上行时数据访问层是 Input,业务逻辑层是 Process,表现层是 Output
接口调用如果是远程调用,那么就构成了简单的分布式
最简单的远程接口实现方式是 Web Service 或 REST
当然,一个合理的分布式应用不仅仅是远程接口调用这么简单,还需要有负载均衡、缓存等功能
实现分布式最简单的技术是 REST 接口,因为 REST 接口可以使用现存的各种服务器,比如使用负载均衡服务器和缓存服务器来实现负载均衡和缓存功能
关于通信协议,不同的公司有不同的选择,但是建议同一个公式内部使用统一的通信协议,比较典型的有 GRPC 和 BRPC
GRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计
目前提供 C、Java 和 Go 语言版本,分别是 GRPC、GRPC-Java、GRPC-Go,其中 C 版本支持 C、C++、NodeJs、Python、Rubby、Objective-C、PHP 和 C#
GRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多重用请求特性,这些特性使得其在移动设备上表现更好,更省电和节省空间占用
与 GRPC 类似,BRPC 源自百度,目前支撑百度内部大约 75 万个在线的实例
其实基于以上的集中选择都能够完成高校的开发,团队内部使用统一的标准,这样更有利于模块化和统一标准
服务间的通信通过轻量级的 Web 服务,使用同步的 REST API 进行通信
在实际的项目应用中,一般推荐在查询是使用同步机制,在增删改时使用异步的方式,结合消息队列来实现数据的操作,以保证最终的数据一致性
REST API 应为创建、检索、更新和删除操作使用标准 HTTP 动词,而且应特别注意操作是否幂等
POST 操作可用于创建资源。POST 操作的明显特征是它不是幂等的。举例而言,如果使用 POST 请求创建资源,而且启动该请求多次,那么每次调用后都会创建一个新的唯一资源
GET 操作必须是幂等的且不会产生意外结果。具体来讲,带有查询参数的 GET 请求不应用于更改或更新信息(而应使用 POST、PUT 或 PATCH)
PUT 操作可用于更新资源。PUT 操作通常包含要更新的资源的完整副本,使该操作具有幂等性
PATCH 操作允许对资源执行部分更新。它们不一定是幂等的,具体取决于如何指定增量并应用到资源上。例如,如果一个 PATCH 操作表明一个值应从 A 改为 B,那么它就是幂等的。若它已经启动多次而且值已是 B,则没有任何效果。对 PATCH 操作的支持仍不一致。例如,JavaEE 7 中的 JAX-RS 中没有 @PATCH 注解
DELETE 操作用于删除资源。删除操作是幂等的,因为资源只能删除一次。但是,返回代码不同,因为第一次操作将成功(200),而后续调用不会找到资源(204)
参考资料:《微服务架构实战》—— 张锋
一 叶 知 秋,奥 妙 玄 心