Books - Domain-Driven Refactoring - 4

Part 2 Refactoring Legacy Systems

This second part of the book is all about helping you take control of legacy systems. You’ll learn practical refactoring principles, discover how to bring order to chaotic architectures, and explore techniques such as CQRS and event-driven design. You’ll also cover refactoring databases and how to use DDD patterns to enable continuous integration and continuous deployment (CI/CD). By the time you finish, you’ll be ready to transform even the most stubborn systems into something maintainable and future-proof.

This part of the book includes the following chapters:

Chapter 5, Introducing Refactoring Principles

Chapter 6, Transitioning from Chaos

Chapter 7, Integrating Events with CQRS

Chapter 8, Refactoring the Database

Chapter 9, DDD Patterns for Continuous Integration and Continuous Refactoring

Chapter 5 Introducing Refactoring Principles

重构原则介绍

In the previous chapter, we reviewed the tactical patterns of DDD and equipped you with a robust toolkit to model complex systems effectively. As you now understand, tactical patterns are crucial for aligning your software architecture with the core business domain. However, as systems evolve, even well-designed code bases can accumulate technical debt and become harder to maintain. This is where refactoring comes into play.

在上一章中,我们回顾了 DDD 的战术模式,并为您提供了一个强大的工具包来有效地对复杂系统进行建模。正如您现在所了解的,战术模式对于使您的软件架构与核心业务领域保持一致至关重要。然而,随着系统的发展,即使是设计良好的代码库也会积累技术债务并变得更难维护。这就是重构发挥作用的地方。

Starting with this chapter, you will embark on the journey of mastering refactoring principles, a critical skill set for any software developer working within the DDD paradigm. Refactoring is not merely about making your code cleaner—it’s about making it more aligned with the underlying business needs, more maintainable, and more resilient to future changes.

从本章开始,您将踏上掌握重构原理的旅程,这是任何在 DDD 范式中工作的软件开发人员的关键技能。重构不仅仅是让你的代码更简洁,而是让它更符合底层业务需求,更易于维护,并且对未来的变化更具弹性。

The chapter is structured around three core themes:

本章围绕三个核心主题构建:

Understanding before acting

行动前先了解

The pillars of safe change

安全变革的支柱

Toward cleaner and more maintainable code

实现更干净、更易于维护的代码

You will first learn the importance of thoroughly analyzing and comprehending the existing code base. This includes gaining insights into the current design, recognizing potential pitfalls, and identifying the right tools to facilitate the refactoring process. You will develop the skills to evaluate code with a critical eye, ensuring that every change is purposeful and aligned with the domain model.

您将首先了解彻底分析和理解现有代码库的重要性。这包括深入了解当前设计、识别潜在陷阱以及确定正确的工具来促进重构过程。您将培养以批判性的眼光评估代码的技能,确保每次更改都是有目的的并与领域模型保持一致。

Refactoring can be risky if not performed carefully. You will learn how to establish a safety net through the use of testing patterns, ensuring that every step of your refactoring journey is validated by reliable tests. This section will teach you how to create, maintain, and use these tests to detect unintended consequences early, minimizing risks and preserving the integrity of your application.

如果不仔细执行,重构可能会有风险。您将学习如何通过使用测试模式来建立安全网,确保重构之旅的每一步都通过可靠的测试进行验证。本节将教你如何创建、维护和使用这些测试,以便及早发现意外后果,从而最大限度地降低风险并保持应用程序的完整性。

Finally, you will explore the application of design principles that guide effective incremental refactoring. By applying these principles, you will learn how to evolve your code base incrementally, making small, manageable changes that gradually lead to a cleaner, more maintainable, and more scalable system.

最后,您将探索指导有效增量重构的设计原则的应用。通过应用这些原则,您将学习如何逐步发展代码库,进行小的、可管理的更改,逐渐形成更干净、更易于维护和更具可扩展性的系统。

The complete code for this chapter can be found within the 01-monolith_legacy branch within the book’s GitHub repository here:  GitHub - PacktPublishing/Domain-driven-Refactoring at 01-monolith_legacy .

Understanding before acting

行动前先了解

If you take a look at the 01-monolith_legacy branch of our ERP example, you’ll notice that the code is somewhat messy. While the project names are clear, it’s difficult to identify any architectural pattern, such as Onion Architecture, Clean Architecture, or even Hexagonal Architecture.

如果你看一下我们 ERP 示例的 01-monolith_legacy 分支,你会发现代码有些混乱。虽然项目名称很明确,但很难识别任何架构模式,例如洋葱架构、清洁架构,甚至六边形架构。

façade  立面

A façade class offers a simplified interface to a complicated subsystem made up of many interconnected components. Although it simplifies access, a façade class may only offer partial functionality compared to interacting directly with the subsystem.

门面类为由许多互连组件组成的复杂子系统提供了一个简化的接口。尽管它简化了访问,但与直接与子系统交互相比, 外观类可能仅提供部分功能。

Challenges of Tight Coupling in Sales and Warehouse Services

销售和仓库服务中紧密耦合的挑战

A major challenge with refactoring lies in the intricate web of dependencies between objects. When you modify one object, these dependencies often force you to make changes in others, creating a cascading effect. Therefore, before diving into refactoring, it’s crucial to identify the boundaries within different contexts. Understanding these boundaries equips you with the tools needed for effective refactoring.

重构的一个主要挑战在于对象之间错综复杂的依赖关系网络。当您修改一个对象时,这些依赖关系通常会迫使您对其他对象进行更改,从而产生级联效应。因此,在深入研究重构之前,确定不同上下文中的边界至关重要。了解这些边界可以为您提供有效重构所需的工具。

Figure 5.1 illustrates the ripple effect that any modification, regardless of the layer, has on the entire solution. This is one of the biggest challenges in maintaining and evolving a code base. Every time you need to introduce a new feature, you are forced to modify the entire solution. Given that no one fully understands the entire code base, there is a constant fear of introducing regressions or bugs. This fear often becomes a constraint, leading developers to resist making necessary changes.

图 5.1 说明了任何修改(无论层如何)对整个解决方案产生的连锁反应。这是维护和发展代码库的最大挑战之一。每次需要引入新功能时,您都被迫修改整个解决方案。鉴于没有人完全理解整个代码库,人们一直担心引入回归或错误。这种恐惧常常成为一种限制,导致开发人员抵制进行必要的更改。

Figure 5.1 – How changes impact all the monolith’s layers

图 5.1 – 更改如何影响单体的所有层

Attempting to transform a monolithic architecture into something different, such as a microservices architecture, in just one step will surely backfire on you. Gall’s law reminds us that complex systems evolve from simpler ones, emphasizing the importance of gradual transformation:

试图在一步内将单体架构转换为不同的东西,例如微服务架构,肯定会适得其反。高尔定律提醒我们,复杂的系统是从简单的系统演变而来的,强调了逐步转变的重要性:

“A complex system that works is invariably found to have evolved from a simple system that worked. The inverse proposition also appears to be true: a complex system designed from scratch never works and cannot be made to work. You have to start over, beginning with a working simple system.”

“一个有效的复杂系统总是被发现是从一个简单的有效的系统演变而来的。相反的命题似乎也是正确的:从头开始设计的复杂系统永远不会起作用,也无法工作。你必须重新开始,从一个工作简单的系统开始。

The key to a successful evolution of your monolithic application relies on proceeding with small, incremental steps. This evolution starts by reorganizing your code base into modules applying architectural modularity. This is a stepping stone to architectural quantum, as you will see in the coming chapters.

成功发展整体应用程序的关键取决于继续执行小的增量步骤。这种演变首先将代码库重组为应用架构模块化的模块。这是通往建筑量子的垫脚石,正如您将在接下来的章节中看到的那样。

However, what is a module? Modular architecture is based on the idea of breaking down a system into smaller, independent components, or modules, that can be developed, tested, and maintained separately. Within a module, objects can have dependencies on each other, but in this case, we are talking about cohesion rather than coupling.

但是,什么是模块?模块化架构基于将系统分解为更小的独立组件或模块的想法,这些组件或模块可以单独开发、测试和维护。在模块中,对象可以相互依赖,但在这种情况下,我们谈论的是内聚而不是耦合。

Coupling versus cohesion  耦合与内聚

Cohesion refers to the degree of relation of the elements within a module, or component of a software system, that work together to achieve a common purpose. On the other hand, coupling refers to the degree of interdependence between different modules in a software system.

内聚力是指模块或软件系统组件内元素之间的关系程度,这些元素协同工作以实现共同目的。另一方面,耦合是指软件系统中不同模块之间的相互依赖程度。

The primary goal of modularity is to enable teams to make changes to one part of the system without impacting others. This approach minimizes the risk of changes cascading through the system and causing unexpected issues.

模块化的主要目标是使团队能够在不影响其他部分的情况下对系统的某一部分进行更改。这种方法最大限度地降低了更改在系统中级联并导致意外问题的风险。

After adopting a modular architecture, it’s crucial to establish clear communication methods between modules. Since your goal is to eliminate dependencies, implementing Consumer-Driven Contracts (CDCs) is essential. CDCs are agreements between service providers and consumers that specify how services should interact. These contracts are versioned and validated to ensure backward compatibility, allowing services to evolve independently without disrupting consumers. By explicitly defining and testing these interactions, CDCs support safe and incremental changes.

采用模块化架构后,在模块之间建立清晰的通信方式至关重要。由于您的目标是消除依赖关系,因此实施消费者驱动的合同 (CDC) 至关重要。CDC 是服务提供商和消费者之间的协议,规定了服务应如何交互。这些合约经过版本控制和验证,以确保向后兼容性,允许服务独立发展而不会中断消费者。通过明确定义和测试这些交互,CDC 支持安全和增量更改。

Incremental change involves making small, manageable adjustments to the system, rather than implementing large, sweeping modifications all at once. By breaking down changes into smaller increments, the scope of potential failure is significantly reduced, which makes it easier to isolate, test, and resolve issues before they escalate. This methodical approach allows for a more thorough understanding of the impact of each change, minimizing the risk of introducing new bugs or disruptions.

增量变革涉及对系统进行小的、可管理的调整,而不是一次性实施大规模的、全面的修改。通过将更改分解为更小的增量,可以显着减少潜在故障的范围,从而更容易在问题升级之前隔离、测试和解决问题。这种有条不紊的方法可以更全面地了解每次更改的影响,从而最大限度地降低引入新错误或中断的风险。

Moreover, incremental changes align well with continuous delivery and deployment practices. By integrating and deploying smaller updates frequently, teams can quickly roll out new features and fixes in a controlled manner. This reduces the time between development and production, allowing teams to respond swiftly to feedback and adapt to evolving requirements. The ability to make safe, incremental changes is crucial in maintaining system stability and ensuring that software can evolve over time without compromising quality or performance.

此外,增量更改与持续交付和部署实践非常吻合。通过频繁集成和部署较小的更新,团队可以以受控的方式快速推出新功能和修复程序。这减少了开发和生产之间的时间,使团队能够快速响应反馈并适应不断变化的需求。进行安全、增量更改的能力对于维护系统稳定性并确保软件能够随着时间的推移而发展而不影响质量或性能至关重要。

Before making any changes to your code base, it’s crucial to prepare the necessary tests to ensure your modifications don’t disrupt existing functionality. Automated testing involves creating and maintaining a suite of tests that cover various aspects of the application, such as unit, integration, End-to-End (E2E), and performance tests. Although it may not be feasible to cover all types of tests at this stage, prioritizing E2E tests is essential. The primary goal of refactoring is to improve the internal structure of the code without changing its external behavior. Automated testing acts as a safety net, helping to identify any regressions or issues that arise during the refactoring process, thereby ensuring that changes do not introduce new bugs or break existing features.

在对代码库进行任何更改之前,准备必要的测试以确保您的修改不会破坏现有功能至关重要。自动化测试涉及创建和维护一套涵盖应用程序各个方面的测试,例如单元、集成、 端到端 (E2E) 和性能测试。尽管现阶段可能无法涵盖所有类型的测试,但优先考虑 E2E 测试至关重要。重构的主要目标是在不改变其外部行为的情况下改进代码的内部结构。自动化测试充当安全网,帮助识别重构过程中出现的任何回归或问题,从而确保更改不会引入新错误或破坏现有功能。

Establishing CI/CD and Observability in Refactoring

在重构中建立 CI/CD 和可观测性

Another key aspect of the refactoring process is implementing Continuous Integration and Continuous Deployment (CI/CD). Legacy applications often lack these automated processes, so one of your goals should be to establish them. CI/CD pipelines automate the building, testing, and deployment of software. CI ensures that changes are frequently integrated, while CD automates the release of those changes to production. These practices shorten the time between making a change and seeing its impact in production, promoting rapid feedback and minimizing the risks associated with deploying updates.

重构过程的另一个关键方面是实施持续集成和持续部署 (CI/CD)。遗留应用程序通常缺乏这些自动化流程,因此您的目标之一应该是建立它们。CI/CD 管道可自动执行软件的构建、测试和部署。CI 确保经常集成更改,而 CD 自动将这些更改发布到生产环境。这些做法缩短了从进行更改到看到其在生产中的影响之间的时间,促进了快速反馈并最大限度地降低了与部署更新相关的风险。

Last but not least, once you have automated all the processes to build and deploy your application, you need to observe and monitor it. Observability involves the tools and processes to monitor the health and performance of the system in real time. The fundamental pillars of observability include logging, metrics, and tracing. Observability allows teams to detect and respond to issues quickly, minimizing the impact of any problems that arise from changes. It also provides insights into the behavior of the system, which can inform future changes.

最后但并非最不重要的一点是,一旦您自动化了构建和部署应用程序的所有流程,您就需要观察和监控它。可观测性涉及实时监控系统运行状况和性能的工具和流程。可观测性的基本支柱包括日志记录、指标和跟踪。可观测性使团队能够快速检测和响应问题,从而最大限度地减少因更改而引起的任何问题的影响。它还提供了对系统行为的洞察,这可以为未来的更改提供信息。

These pillars work together to create a framework that supports the safe evolution of software systems. By adhering to these principles, teams can make changes with confidence, knowing that they have mechanisms in place to catch issues early and mitigate risks. This approach is essential in complex, distributed environments where changes are frequent, and the impact of failures can be significant.

这些支柱共同创建了一个支持软件系统安全发展的框架。通过遵守这些原则,团队可以充满信心地做出改变,因为他们知道他们有适当的机制来及早发现问题并降低风险。这种方法在复杂的分布式环境中至关重要,因为在这些环境中,更改频繁且故障的影响可能很大。

During a refactor, developers often feel tempted to implement new patterns or frameworks they have learned, driven by both their ego (who hasn’t been there?) and the belief that patterns are the key to writing better code. However, this approach can be a double-edged sword. The following chart illustrates the idea that the more senior a developer becomes, the less complex their code tends to be.

在重构过程中,开发人员经常受到诱惑,想要实现他们学到的新模式或框架,这既是由他们的自我(谁没有去过那里呢?)和模式是编写更好代码的关键的信念驱动的。然而,这种方法可能是一把双面刃。下图说明了这样一种观点,即开发人员的资深程度越高,他们的代码往往就越不复杂。

Figure 5.2 – Code complexity versus experience

图 5.2 – 代码复杂性与体验

You should have noticed that at the beginning of the Experience axis, we wrote Super easy code, while at the end, it says Super simple code. That’s because writing easy code is in no way the same as writing simple code. The former just works but it is not well structured, maintainable, or readable while the latter tends to be all of them while maintaining simplicity.

您应该已经注意到,在体验轴的开头,我们写了超级简单的代码 ,而在最后,它说的是超级简单的代码 。这是因为编写简单的代码与编写简单的代码绝不相同。前者只是工作,但结构不好、不可维护或可读,而后者往往在保持简单性的同时成为所有这些。

As stated by Neal Ford in his book The Productive Programmer, “Developers are drawn to complexity, like moths to a flame, often with the same outcome.”

正如 Neal Ford 在他的《 生产程序员》 一书中所说,“ 开发人员被复杂性所吸引,就像飞蛾扑火一样,通常会得到相同的结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜流冰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值