Books - Domain-Driven Refactoring - 6
本章介绍了重构的核心原则与实践方法,重点讲解了如何通过设计模式创建更清晰、可维护的代码架构。文章系统阐述了CQRS+ES架构模式(命令查询职责分离+事件溯源)的工作原理,并强调通过小步迭代而非一次性重构的重要性。关键部分详细解析了SOLID原则中的SRP(单一职责)、OCP(开闭原则)和DIP(依赖倒置)原则,结合销售订单系统的具体案例,展示了如何运用策略模式实现折扣计算逻辑的解耦。作者指出良好的
·
Chapter 5, Introducing Refactoring Principles
第 5 章 , 重构原则介绍
Towards cleaner and more maintainable code
实现更干净、更易于维护的代码
At the end of our journey, our goal is to refactor the application toward an architecture such as that in Figure 5.5.
在我们的旅程结束时,我们的目标是将应用程序重构为图 5.5 中的架构。

Figure 5.5 – The end result of our refactoring process
图 5.5 – 重构过程的最终结果
This is a well-organized code base. You should have already noticed that we used numbers in the solution folders’ names. We usually do this to clearly indicate the level of every layer of our application and, in our opinion, this follows the Onion Architecture pattern more closely. The solution folder we are referring to is just a logical folder that you can create to organize your projects inside IDEs such as Microsoft Visual Studio or JetBrains Rider. Obviously, you can easily achieve the same result with other frameworks and languages by using physical folders instead of logical ones.
这是一个组织良好的代码库。您应该已经注意到,我们在解决方案文件夹的名称中使用了数字。我们通常这样做是为了清楚地指示应用程序每一层的级别,在我们看来,这更紧密地遵循洋葱架构模式。我们所指的解决方案文件夹只是一个逻辑文件夹,您可以创建该文件夹以在 Microsoft Visual Studio 或 JetBrains Rider 等 IDE 中组织您的项目。显然,您可以通过使用物理文件夹而不是逻辑文件夹轻松地使用其他框架和语言实现相同的结果。
A well-organized code base benefits all team members, including newcomers. It immediately clarifies the separation of concerns when opening the solution, making it easier to identify which component or module needs to be touched to introduce a new feature or solve a bug. Additionally, the presence of tests at every level reduces the fear of introducing regressions.
组织良好的代码库使所有团队成员受益,包括新成员。它立即明确了打开解决方案时关注点的分离,从而更容易识别需要触摸哪个组件或模块来引入新功能或解决错误。此外,每个级别的测试都减少了对引入回归的恐惧。
Also, an important thing you should have noticed right away is how the modules folder clearly represents the bounded contexts we defined and created in Chapter 3.
此外,您应该立即注意到的一件重要事情是 modules 文件夹如何清楚地表示我们在第 3 章中定义和创建的有界上下文。
Last but not least, Figure 5.5 represents an event-driven approach paired with Command Query Responsibility Segregation (CQRS).
最后但并非最不重要的一点是, 图 5.5 表示了与命令查询责任隔离 (CQRS) 配对的事件驱动方法。
The term CQRS was coined by Greg Young in 2010. This architectural pattern is based on the principle of separating the responsibilities of commands (which modify state) and queries (which retrieve data) within a system. Young’s introduction of CQRS built upon Command Query Separation (CQS), which was originally defined by Bertrand Meyer in his 1988 book, Object-Oriented Software Construction.
CQRS 一词由 Greg Young 于 2010 年创造。这种架构模式基于将系统内命令(修改状态)和查询(检索数据)的职责分开的原则。Young 引入的 CQRS 建立在命令查询分离 (CQS) 之上,CQS 最初由 Bertrand Meyer 在其 1988 年出版的《 面向对象软件构建 》一书中定义。
In our specific case, the commands are found in BrewUp.Sales.Domain and BrewUp.Warehouses.Domain, while the queries part are handled by BrewUp.Sales.ReadModel and BrewUp.Warehouses.ReadModel.
在我们的具体情况下,命令位于 BrewUp.Sales.Domain 和 BrewUp.Warehouses.Domain 中,而查询部分由 BrewUp.Sales.ReadModel 和 BrewUp.Warehouses.ReadModel 处理。
CQRS is often used in conjunction with event sourcing to enhance system architecture by providing clear boundaries between read and write operations, thereby improving scalability and maintainability, hence the definition of CQRS+ES.
CQRS 通常与事件溯源结合使用,通过在读写作之间提供清晰的边界来增强系统架构,从而提高可扩展性和可维护性,因此定义了 CQRS+ES。
In Figure 5.6, you can see how a CQRS+ES system works.
在图 5.6 中,您可以看到 CQRS+ES 系统的工作原理。

Figure 5.6 – CQRS+ES diagram
图 5.6 – CQRS+ES 图
In Chapter 7, Integrating Events with CQRS, we will start the refactoring process by implementing CQRS and then add events for communication between bounded contexts.
在第 7 章 “ 将事件与 CQRS 集成” 中,我们将通过实现 CQRS 来启动重构过程,然后添加用于在边界上下文之间进行通信的事件。
As with all things in life, to be able to solve or work on large complex problems, you must break them down into smaller ones. As far as our architecture is concerned, it is not advisable to refactor directly to CQRS+ES in one step, but instead, as reiterated multiple times throughout this book, take small steps.
与生活中的所有事情一样,为了能够解决或处理大型复杂问题,您必须将它们分解为较小的问题。就我们的架构而言,不建议一步直接重构到 CQRS+ES,而是像本书中多次重申的那样,采取小步骤。
To conclude our tour of the tools we need in our toolbox to be able to do what we described, we must review the last principles essential to successful refactoring. These design patterns play a pivotal role in ensuring that the system remains flexible, maintainable, and scalable.
为了结束我们对工具箱中能够执行我们所描述的作所需的工具的浏览,我们必须回顾成功重构所必需的最后原则。这些设计模式在确保系统保持灵活性、可维护性和可扩展性方面发挥着关键作用。
Single responsibility principle
单一责任原则
One of the foundational principles is the Single Responsibility Principle (SRP). The SRP states that a class or module should have only one reason to change, meaning it should be responsible for only one aspect of the system’s functionality. This is different from the common misconception that SRP means a class should simply ‘do one thing.’ Instead, it means that all the responsibilities of the class should align with a single, well-defined reason for change. When your code adheres to the SRP, it becomes easier to understand, test, and maintain because each component has a clear purpose. In the context of DDD, this principle ensures that your code reflects the distinct concepts of the domain, making the system more intuitive and aligned with business needs. It is essential for refactoring because it helps to break down complex, monolithic classes into smaller, more manageable pieces. Each class handles a specific aspect of the functionality, making the system easier to maintain and extend.
基本原则之一是单一责任原则 (SRP)。SRP 规定,一个类或模块应该只有一个更改的原因,这意味着它应该只负责系统功能的一个方面。这与常见的误解不同,即 SRP 意味着一个类应该简单地“做一件事”。相反,这意味着班级的所有职责都应该与一个单一的、明确定义的变革原因保持一致。当您的代码遵守 SRP 时,它会变得更容易理解、测试和维护,因为每个组件都有明确的用途。在 DDD 的上下文中,此原则可确保您的代码反映域的不同概念,使系统更加直观并符合业务需求。它对于重构至关重要,因为它有助于将复杂的整体类分解为更小、更易于管理的部分。每个类都处理功能的特定方面,使系统更易于维护和扩展。
Using SalesOrderService and WarehouseService, as shown earlier in this chapter, we could refactor them as follows:
使用 SalesOrderService 和 WarehouseService,如本章前面所示,我们可以按如下方式重构它们:
public class SalesOrderService
{
private readonly IWarehouseService _warehouseService;
public SalesOrderService(IWarehouseService warehouseService)
{
_warehouseService = warehouseService;
}
public void ProcessOrder(Order order)
{
if (_warehouseService.IsBeerAvailable(order.BeerId, order.Quantity))
{
// Process the order
}
else
{
// Handle out-of-stock scenario
}
}
}
public interface IWarehouseService
{
bool IsBeerAvailable(int beerId, int quantity);
}
public class WarehouseService : IWarehouseService
{
public bool IsBeerAvailable(int beerId, int quantity)
{
// Check availability in the warehouse
return true; // or false
}
}
Now, SalesOrderService is only responsible for processing orders, while WarehouseService only handles inventory checks, adhering to the SRP.
现在,SalesOrderService 只负责处理订单,而 WarehouseService 只处理库存检查,遵守 SRP。
Open/closed principle
Another critical principle is the Open/Closed Principle (OCP), which posits that software entities should be open for extension but closed for modification. This means that the behavior of a class can be extended without modifying its source code. By adhering to the OCP during refactoring, you protect your system from the unintended consequences that often come with modifying established code. This approach not only preserves the integrity of your application but also facilitates the gradual introduction of new features.
另一个关键原则是开放/封闭原则 (OCP),它假设软件实体应该开放以进行扩展,但关闭以进行修改。这意味着可以在不修改其源代码的情况下扩展类的行为。通过在重构期间遵守 OCP,可以保护系统免受修改已建立代码通常带来的意外后果的影响。这种方法不仅可以保持应用程序的完整性,还可以促进新功能的逐步引入。
Imagine we need to add a discount feature to SalesOrderService. Instead of modifying the existing code, we can extend the service using the OCP:
假设我们需要向 SalesOrderService 添加折扣功能。我们可以使用 OCP 扩展服务,而不是修改现有代码:
public class SalesOrderService
{
private readonly IWarehouseService _warehouseService;
private readonly IDiscountStrategy _discountStrategy;
public SalesOrderService(IWarehouseService warehouseService, IDiscountStrategy discountStrategy)
{
_warehouseService = warehouseService;
_discountStrategy = discountStrategy;
}
public void ProcessOrder(Order order)
{
if (_warehouseService.IsBeerAvailable(order.BeerId, order.Quantity))
{
decimal discount = _discountStrategy.CalculateDiscount(order);
// Apply discount and process the order
}
else
{
// Handle out-of-stock scenario
}
}
}
public interface IDiscountStrategy
{
decimal CalculateDiscount(Order order);
}
public class NoDiscountStrategy : IDiscountStrategy
{
public decimal CalculateDiscount(Order order)
{
return 0;
}
}
public class SeasonalDiscountStrategy : IDiscountStrategy
{
public decimal CalculateDiscount(Order order)
{
// Implement seasonal discount logic
return 10; // Placeholder discount value
}
}
Here, we’ve introduced an IDiscountStrategy interface and implemented different discount strategies. We can now easily introduce new discount types without modifying the existing SalesOrderService logic.
在这里,我们引入了一个 IDiscountStrategy 接口并实现了不同的折扣策略。现在,我们可以轻松引入新的折扣类型,而无需修改现有的 SalesOrderService 逻辑。
Dependency inversion principle
依赖倒置原理
The Dependency Inversion Principle (DIP) is another powerful tool in your refactoring arsenal. The DIP suggests that high-level modules should not depend on low-level modules; instead, both should depend on abstractions. This principle helps in decoupling your code, making it more modular and flexible. During refactoring, applying the DIP allows you to isolate changes to specific parts of the system, minimizing the ripple effect that modifications can have. This decoupling is particularly beneficial in large, complex systems where changes in one area should not unnecessarily impact others.
依赖反转原则 (DIP) 是重构武器库中的另一个强大工具。DIP 建议高级模块不应依赖于低级别模块;相反,两者都应该依赖于抽象。这一原则有助于解耦您的代码,使其更加模块化和灵活。在重构期间,应用 DIP 可以隔离对系统特定部分的更改,从而最大限度地减少修改可能产生的连锁反应。这种解耦在大型、复杂的系统中特别有益,因为一个领域的变化不应对其他领域产生不必要的影响。
The introduction of the IWarehouseService interface inside the constructor of SalesOrderService allows decoupling from the concrete implementation and allows us to create mock classes to fully unit test SalesOrderService.
在 SalesOrderService 的构造函数中引入 IWarehouseService 接口允许与具体实现解耦,并允许我们创建模拟类来完全单元测试 SalesOrderService。
Strategy pattern 策略模式
The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. The pattern allows the algorithm to vary independently from the clients that use it.
策略模式定义了一系列算法,封装了每个算法,并使它们可以互换。该模式允许算法独立于使用它的客户端而变化。
This matters because this pattern is valuable when you have multiple ways to perform a certain task and want to make the system easily extendable with new strategies without modifying existing code.
这很重要,因为当您有多种方法来执行特定任务并希望在不修改现有代码的情况下使用新策略轻松扩展系统时,这种模式很有价值。
Let’s expand our discount calculation logic using the Strategy pattern:
让我们使用策略模式扩展我们的折扣计算逻辑:
public interface IDiscountStrategy
{
decimal CalculateDiscount(Order order);
}
public class NoDiscountStrategy : IDiscountStrategy
{
public decimal CalculateDiscount(Order order)
{
return 0;
}
}
public class PercentageDiscountStrategy : IDiscountStrategy
{
private readonly decimal _percentage;
public PercentageDiscountStrategy(decimal percentage)
{
_percentage = percentage;
}
public decimal CalculateDiscount(Order order)
{
return order.TotalAmount * _percentage;
}
}
public class SalesOrderService
{
private readonly IWarehouseService _warehouseService;
private readonly IDiscountStrategy _discountStrategy;
public SalesOrderService(IWarehouseService warehouseService, IDiscountStrategy discountStrategy)
{
_warehouseService = warehouseService;
_discountStrategy = discountStrategy;
}
public void ProcessOrder(Order order)
{
if (_warehouseService.IsBeerAvailable(order.BeerId, order.Quantity))
{
decimal discount = _discountStrategy.CalculateDiscount(order);
// Apply discount and process the order
}
else
{
// Handle out-of-stock scenario
}
}
}
In the preceding example, SalesOrderService can work with any discount strategy without needing to know the specifics of how discounts are calculated.
在前面的示例中,SalesOrderService 可以使用任何折扣策略,而无需了解折扣计算方式的具体信息。
By embracing the principles and techniques we have discussed in this section, you will be able to systematically transform your code into a cleaner, more maintainable state. Each refactoring effort will contribute to a code base that not only better supports the current business requirements but is also more resilient and adaptable to future changes. Ultimately, this approach ensures that your system remains agile, responsive, and closely aligned with the evolving needs of the business domain.
通过采用我们在本节中讨论的原则和技术,您将能够系统地将代码转换为更干净、更易于维护的状态。每一次重构工作都将有助于建立一个代码库,该代码库不仅可以更好地支持当前的业务需求,而且更具弹性和适应性,以适应未来的变化。最终,这种方法可确保您的系统保持敏捷、响应迅速,并与业务领域不断变化的需求紧密结合。
In addition, the modular architecture we are working on will lead to the possibility of moving to a microservices architecture with less effort (you will read about it in Part 3 of this book).
此外,我们正在研究的模块化架构将导致以更少的努力迁移到微服务架构的可能性(您将在本书的第 3 部分中阅读)。
Summary 总结
This chapter has equipped you with a solid foundation in the principles and practices of refactoring within a DDD context. You learned that refactoring is not just about making code cleaner but also about aligning it more closely with the underlying business needs, enhancing its maintainability, and ensuring its resilience to future changes.
本章为你提供了在 DDD 上下文中重构的原则和实践的坚实基础。您了解到,重构不仅仅是让代码更简洁,还是为了使其更紧密地与底层业务需求保持一致,增强其可维护性,并确保其对未来变化的弹性。
You learned about the importance of thoroughly analyzing your code base before taking any action, recognizing potential pitfalls, and selecting the appropriate tools for the job. The chapter highlighted the critical role of testing in the refactoring process, emphasizing how a robust suite of tests acts as a safety net, allowing you to make changes with confidence that your application’s integrity will be preserved.
您了解了在采取任何行动之前彻底分析代码库、识别潜在陷阱以及为工作选择适当工具的重要性。本章强调了测试在重构过程中的关键作用,强调了一套强大的测试如何充当安全网,使您能够放心地进行更改,从而保证应用程序的完整性将得到保留。
Furthermore, you explored the application of key design principles, such as the SRP, OCP, DIP, and Strategy pattern. These principles will guide you in making incremental, manageable changes that lead to a cleaner, more maintainable, and more scalable system. The practical examples provided showed you how to tackle common challenges in refactoring, such as reducing tight coupling between services and transitioning toward a modular architecture.
此外,您还探索了关键设计原则的应用,例如 SRP、OCP、DIP 和策略模式。这些原则将指导您进行渐进式、可管理的更改,从而形成更干净、更易于维护和更可扩展的系统。提供的实际示例向您展示了如何解决重构中的常见挑战,例如减少服务之间的紧密耦合和过渡到模块化架构。
As you move forward, the skills you’ve acquired—critical code analysis, comprehensive testing strategies, and the application of design principles—will empower you to refactor your code base effectively. This will ensure that your system remains agile, adaptable, and aligned with the evolving needs of your business domain, setting the stage for continued success in software development.
随着您的前进,您所获得的技能(关键代码分析、全面的测试策略和设计原则的应用)将使您能够有效地重构代码库。这将确保您的系统保持敏捷、适应性强,并符合业务领域不断变化的需求,为软件开发的持续成功奠定基础。
In the next chapter, we will start to refactor our monolithic code base into a well-organized modular monolith.
在下一章中,我们将开始将我们的单体代码库重构为一个组织良好的模块化单体。
更多推荐
所有评论(0)