您现在访问的是微软AZURE全球版技术文档网站,若需要访问由世纪互联运营的MICROSOFT AZURE中国区技术文档网站,请访问 https://docs.azure.cn.

进化型设计Design for evolution

进化型设计是持续创新的关键An evolutionary design is key for continuous innovation

无论是修复 bug、添加新功能、引入新技术,还是使现有系统更具伸缩性和弹性,所有成功的应用程序都在随着时间而不断变化。All successful applications change over time, whether to fix bugs, add new features, bring in new technologies, or make existing systems more scalable and resilient. 如果应用程序的所有部分都紧密耦合,则很难将更改引入系统。If all the parts of an application are tightly coupled, it becomes very hard to introduce changes into the system. 应用程序中一个部分的更改可能会破坏另一部分,或者改变整个代码库。A change in one part of the application may break another part, or cause changes to ripple through the entire codebase.

这个问题并不局限于单片应用程序。This problem is not limited to monolithic applications. 应用程序可分解为服务,但仍会表现出那种紧密耦合性,使系统变得刚性和脆弱。An application can be decomposed into services, but still exhibit the sort of tight coupling that leaves the system rigid and brittle. 但当服务设计为可以改进时,团队可以创新并不断提供新功能。But when services are designed to evolve, teams can innovate and continuously deliver new features.

微服务正在成为实现进化设计的一种热门方式,因为它们可以解决此处列出的许多值得注意的问题。Microservices are becoming a popular way to achieve an evolutionary design, because they address many of the considerations listed here.

建议Recommendations

强制执行高度内聚和松散耦合Enforce high cohesion and loose coupling. 如果服务提供逻辑上具有共同所属的功能,则该服务具有内聚性。A service is cohesive if it provides functionality that logically belongs together. 如果可以在更改一个服务的同时不会更改另一个服务,则服务具有松散耦合性。Services are loosely coupled if you can change one service without changing the other. 高度内聚通常意味着更改一个函数时还需更改其他相关函数。High cohesion generally means that changes in one function will require changes in other related functions. 如果发现更新某个服务时需要对其他服务进行协调更新,则可能表示该服务不具有内聚性。If you find that updating a service requires coordinated updates to other services, it may be a sign that your services are not cohesive. 域驱动设计 (DDD) 的目标之一就是标识这些边界。One of the goals of domain-driven design (DDD) is to identify those boundaries.

封装域知识Encapsulate domain knowledge. 客户端使用服务时,强制执行域的业务规则的责任不应落在客户端上。When a client consumes a service, the responsibility for enforcing the business rules of the domain should not fall on the client. 相反,服务应封装属于其责任范围内的所有域知识。Instead, the service should encapsulate all of the domain knowledge that falls under its responsibility. 否则,每个客户端都必须强制执行业务规则,最终域知识会分散在应用程序的不同部分。Otherwise, every client has to enforce the business rules, and you end up with domain knowledge spread across different parts of the application.

使用异步消息传递Use asynchronous messaging. 异步消息传递是一种将消息创建者与使用者分离的方法。Asynchronous messaging is a way to decouple the message producer from the consumer. 创建者不依赖于使用者回复消息或采取任何特定操作。The producer does not depend on the consumer responding to the message or taking any particular action. 有了 pub/sub 体系结构,创建者甚至可能不知道谁在使用消息。With a pub/sub architecture, the producer may not even know who is consuming the message. 新服务可以轻松地使用消息,而不需要对创建者进行任何修改。New services can easily consume the messages without any modifications to the producer.

不要将域知识构建到网关中Don't build domain knowledge into a gateway. 在微服务体系结构中,网关对于请求路由、协议转换、负载均衡或身份验证等操作非常有用。Gateways can be useful in a microservices architecture, for things like request routing, protocol translation, load balancing, or authentication. 但网关应该仅限于这种基础结构功能。However, the gateway should be restricted to this sort of infrastructure functionality. 它不应实施任何域知识,以避免成为严重的依赖项。It should not implement any domain knowledge, to avoid becoming a heavy dependency.

公开开放接口Expose open interfaces. 避免在服务之间创建自定义转换层。Avoid creating custom translation layers that sit between services. 相反,服务应该公开具有明确定义的 API 协定的 API。Instead, a service should expose an API with a well-defined API contract. API 应拥有版本控制,以便在保持向后兼容性的同时改进 API。The API should be versioned, so that you can evolve the API while maintaining backward compatibility. 这样就可以更新服务,而无需对依赖它的所有上游服务进行协调更新。That way, you can update a service without coordinating updates to all of the upstream services that depend on it. 面向公众的服务应通过 HTTP 公开一个 RESTful API。Public facing services should expose a RESTful API over HTTP. 因性能原因,后端服务可能会使用 RPC 样式的消息传递协议。Backend services might use an RPC-style messaging protocol for performance reasons.

针对服务协定进行设计和测试Design and test against service contracts. 在服务公开了明确定义的 API 后,可以针对这些 API 进行开发和测试。When services expose well-defined APIs, you can develop and test against those APIs. 这样可以开发和测试单个服务,而无需启动所有的依赖服务。That way, you can develop and test an individual service without spinning up all of its dependent services. (当然,仍然可以针对实际服务执行集成和负载测试。)(Of course, you would still perform integration and load testing against the real services.)

分清基础结构与域逻辑Abstract infrastructure away from domain logic. 不要将域逻辑与基础结构相关的功能(如消息传递或暂留)混在一起。Don't let domain logic get mixed up with infrastructure-related functionality, such as messaging or persistence. 否则,更改域逻辑时需要对基础结构层进行更新,反之亦然。Otherwise, changes in the domain logic will require updates to the infrastructure layers and vice versa.

将跨领域问题转移到单独服务上Offload cross-cutting concerns to a separate service. 例如,如果多个服务需要对请求进行身份验证,可将此功能移到各自的服务中。For example, if several services need to authenticate requests, you could move this functionality into its own service. 然后便可改进身份验证服务(例如,通过添加新的身份验证流),而无需涉及使用它的任何服务。Then you could evolve the authentication service — for example, by adding a new authentication flow — without touching any of the services that use it.

独立部署服务Deploy services independently. DevOps 团队可以独立地为应用程序中的其他服务部署单个服务时,更新就会更快,更安全。When the DevOps team can deploy a single service independently of other services in the application, updates can happen more quickly and safely. Bug 修复和新功能便能按更常规的节奏推出。Bug fixes and new features can be rolled out at a more regular cadence. 同时设计应用程序和发布过程以支持独立更新。Design both the application and the release process to support independent updates.