烂翻译系列之学习领域驱动设计 您所在的位置:网站首页 inserts翻译 烂翻译系列之学习领域驱动设计

烂翻译系列之学习领域驱动设计

2023-02-28 21:05| 来源: 网络整理| 查看: 265

Business logic is the most important part of software. It’s the reason the software is being implemented in the first place. A system’s user interface can be sexy and its database can be blazing fast and scalable. But if the software is not useful for the business, it’s nothing but an expensive technology demo.

业务逻辑是软件最重要的部分。这就是软件被实现的首要原因。一个系统的用户界面可以是性感的,它的数据库可以是闪耀的快速和可扩展的。但是如果软件对业务没有用处,那么它就只是一个昂贵的技术演示。

As we saw in Chapter 2, not all business subdomains are created equal. Different subdomains have different levels of strategic importance and complexity. This chapter begins our exploration of the different ways to model and implement business logic code. We will start with two patterns suited for rather simple business logic: transaction script and active record.

正如我们在第2章中看到的,并非所有的业务子域都是平等的。不同的子域有不同层次的战略重要性和复杂性。从本章开始,我们将探索建模和实现业务逻辑代码的不同方法。我们将从两种适合于相当简单的业务逻辑的模式开始:事务脚本和活动记录。

Transaction Script 事务脚本

Organizes business logic by procedures where each procedure handles a single request from the presentation. —Martin Fowler

通过程序(流程)组织业务逻辑,每个程序(流程)处理来自展示(层)的单个请求。—Martin Fowler

A system’s public interface can be seen as a collection of business transactions that consumers can execute, as shown in Figure 5-1. These transactions can retrieve information managed by the system, modify it, or both. The pattern organizes the system’s business logic based on procedures, where each procedure implements an operation that is executed by the system’s consumer via its public interface. In effect, the system’s public operations are used as encapsulation boundaries.

系统的公共接口可以看作是使用者可以执行的业务事务的集合,如图5-1所示。这些事务可以检索由系统管理的信息,也可以修改它,或者两者兼而有之。该模式基于程序(流程)组织系统的业务逻辑,其中每个程序(流程)实现一个操作,该操作由系统的使用者通过其公共接口执行。实际上,系统的公共操作被用作封装边界。

 

Figure 5-1. Transaction script interface

图5-1.  事务脚本接口

Implementation 实现

Each procedure is implemented as a simple, straightforward procedural script. It can use a thin abstraction layer for integrating with storage mechanisms, but it is also free to access the databases directly.

每个程序(流程)都作为一个简单、直观的程序(流程)脚本实现。它可以使用一个很薄的抽象层集成存储机制,但也可以直接访问数据库。

The only requirement procedures have to fulfill is transactional behavior. Each operation should either succeed or fail but can never result in an invalid state. Even if execution of a transaction script fails at the most inconvenient moment, the system should remain consistent—either by rolling back any changes it has made up until the failure or by executing compensating actions. The transactional behavior is reflected in the pattern’s name: transaction script.

程序(流程)必须满足的唯一需求是事务行为。每个操作要么成功,要么失败,但绝不能导致无效状态。即使事务脚本的执行在最困难的时候失败,系统也应该保持一致——要么回滚在失败之前所做的任何更改,要么执行补偿操作。事务行为体现在模式的名称中: 事务脚本。

Here is an example of a transaction script that converts batches of JSON files into XML files:

下面是一个将 JSON 文件转换为 XML 文件的事务脚本示例:

1 DB.StartTransaction(); 2 var job = DB.LoadNextJob(); 3 var json = LoadFile(job.Source); 4 var xml = ConvertJsonToXml(json); 5 WriteFile(job.Destination, xml.ToString(); 6 DB.MarkJobAsCompleted(job); 7 DB.Commit() It’s Not That Easy! 没那么简单!

When I introduce the transaction script pattern in my domain-driven design classes, my students often raise their eyebrows, and some even ask, “Is it worth our time? Aren’t we here for the more advanced patterns and techniques?”

当我在我的领域驱动设计课上介绍事务脚本模式时,我的学生经常会扬起他们的眉毛,有些甚至会问,“这值得我们花时间吗?难道我们不是为了更先进的模式和技术而来吗?”

The thing is, the transaction script pattern is a foundation for the more advanced business logic implementation patterns you will learn in the forthcoming chapters. Furthermore, despite its apparent simplicity, it is the easiest pattern to get wrong. A considerable number of production issues I have helped to debug and fix, in one way or another, often boiled down to a misimplementation of the transactional behavior of the system’s business logic.

问题是,事务脚本模式是您将在后面的章节中学习的更高级业务逻辑实现模式的基础。此外,尽管它看起来很简单,但它是最容易出错的模式。我以这样或那样的方式帮助调试和修复了相当多的生产问题,这些问题常常归结为系统业务逻辑的事务行为的错误实现。

Let’s take a look at three common, real-life examples of data corruption that results from failing to correctly implement a transaction script.

让我们来看一下三个常见的、真实的数据损坏示例,这些数据损坏是由于未能正确实现事务脚本而导致的。

Lack of transactional behavior 缺乏事务行为

A trivial example of failing to implement transactional behavior is to issue multiple updates without an overarching transaction. Consider the following method that updates a record in the Users table and inserts a record into the VisitsLog table:

未能实现事务行为的一个平常例子是在没有总体事务的情况下发出多个更新。思考下面的方法,该方法更新 Users 表中的记录并将记录插入 VisitsLog 表中:

01 public class LogVisit 02 { 03 ... 04 05 public void Execute(Guid userId, DataTime visitedOn) 06 { 07 _db.Execute("UPDATE Users SET last_visit=@p1 WHERE user_id=@p2", 08 visitedOn, userId); 09 _db.Execute(@"INSERT INTO VisitsLog(user_id, visit_date) 10 VALUES(@p1, @p2)", userId, visitedOn); 11 } 12 }

If any issue occurs after the record in the Users table was updated (line 7) but before appending the log record on line 9 succeeds, the system will end up in an inconsistent state. The Users table will be updated but no corresponding record will be written to the VisitsLog table. The issue can be due to anything from a network outage to a database timeout or deadlock, or even a crash of the server executing the process.

如果在 Users 表中的记录更新之后(第7行),但在第9行追加日志记录成功之前发生任何问题,系统最终将处于不一致的状态。将更新Users表,但不会向VisitsLog表写入相应的记录。这个问题可能是由于网络中断、数据库超时或死锁、甚至是执行进程的服务器崩溃等原因造成的。

This can be fixed by introducing a proper transaction encompassing both data changes:

可以通过引入包含两个数据更改的适当事务来解决这个问题:

1 public class LogVisit 2 { 3 ... 4 public void Execute(Guid userId, DataTime visitedOn) 5 { 6 try 7 { 8 _db.StartTransaction(); 9 _db.Execute(@"UPDATE Users SET last_visit=@p1 10 WHERE user_id=@p2", 11 visitedOn, userId); 12 _db.Execute(@"INSERT INTO VisitsLog(user_id, 13 visit_date) 14 VALUES(@p1, @p2)", 15 userId, visitedOn); 16 _db.Commit(); 17 } catch { 18 _db.Rollback(); 19 throw; 20 } 21 } 22 }

The fix is easy to implement due to relational databases’ native support of transactions spanning multiple records. Things get more complicated when you have to issue multiple updates in a database that doesn’t support multirecord transactions, or when you are working with multiple storage mechanisms that are impossible to unite in a distributed transaction. Let’s see an example of the latter case.

由于关系型数据库对跨多条记录的事务的原生支持,这个补丁很容易实现。当您必须在不支持多记录事务的数据库中发布多个更新时,或者当您使用多存储机制时,这些机制不可能在分布式事务中统一时,情况就会变得更加复杂。让我们看一个后一种情况的例子。

Distributed transactions 分布式事务

In modern distributed systems, it’s a common practice to make changes to the data in a database and then notify other components of the system about the changes by publishing messages into a message bus. Consider that in the previous example, instead of logging a visit in a table, we have to publish it to a message bus:

在现代分布式系统中,通常的做法是对数据库中的数据进行更改,然后通过将消息发布到消息总线来通知系统的其他组件发生了这些更改。在前面的示例中,我们不需要在表中记录访问,而是需要将其发布到消息总线:

01 public class LogVisit 02 { 03 ... 04 05 public void Execute(Guid userId, DataTime visitedOn) 06 { 07 _db.Execute("UPDATE Users SET last_visit=@p1 WHERE user_id=@p2", 08 visitedOn,userId); 09 _messageBus.Publish("VISITS_TOPIC", 10 new { UserId = userId, VisitDate = visitedOn }); 11 } 12 }

As in the previous example, any failure occurring after line 7 but before line 9 succeeds will corrupt the system’s state. The Users table will be updated but the other components won’t be notified as publishing to the message bus has failed.

与前面的示例一样,第7行之后但第9行成功之前发生的任何故障都将破坏系统的状态。将更新 Users 表,但不会通知到其他组件,因为发布到消息总线失败。

Unfortunately, fixing the issue is not as easy as in the previous example. Distributed transactions spanning multiple storage mechanisms are complex, hard to scale, error prone, and therefore are usually avoided. In Chapter 8, you will learn how to use the CQRS architectural pattern to populate multiple storage mechanisms. In addition, Chapter 9 will introduce the outbox pattern, which enables reliable publishing of messages after committing changes to another database.

不幸的是,修复这个问题并不像前面的示例那样容易。跨多重存储机制的分布式事务复杂、难以缩放、容易出错,因此通常要避免使用分布式事务。在第8章中,您将学习如何使用 CQRS 架构模式来填充多重存储机制。此外,第9章将介绍发件箱模式,它允许在将更改提交到另一个数据库之后可靠地发布消息。

Let’s see a more intricate example of improper implementation of transactional behavior.

让我们看一个关于事务行为的不正确实现的更复杂的示例。

Implicit distributed transactions

隐式分布式事务

Consider the following deceptively simple method:

思考下面这个看似简单的方法:

1 public class LogVisit 2 { 3 ... 4 public void Execute(Guid userId) 5 { 6 _db.Execute("UPDATE Users SET visits=visits+1 WHERE 7 user_id=@p1", 8 userId); 9 } 10 }

Instead of tracking the last visit date as in the previous examples, this method maintains a counter of visits for each user. Calling the method increases the corresponding counter’s value by 1. All the method does is update one value, in one table, residing in one database. Yet this is still a distributed transaction that can potentially lead to inconsistent state.

该方法不像前面的示例那样跟踪最近的访问日期,而是为每个用户维护一个访问计数器。调用该方法将相应计数器的值增加1。该方法所做的只是更新位于一个数据库中的一个表中的一个值。然而,这仍然是一个分布式事务,可能导致不一致的状态。

This example constitutes a distributed transaction because it communicates information to the databases and the external process that called the method, as demonstrated in Figure 5-2.

这个例子构成了一个分布式事务,因为它向数据库和调用该方法的外部进程传递信息,如图5-2所示。

Figure 5-2. The LogVisit operation updating the data and notifying the caller of the operation’s success or failure

图5-2.  LogVisit 操作更新数据并通知调用者操作的成功或失败

Although the execute method is of type void, that is, it doesn’t return any data, it still communicates whether the operation has succeeded or failed: if it failed, the caller will get an exception. What if the method succeeds, but the communication of the result to the caller fails? For example:

尽管 execute 方法的类型为 void,也就是说,它不返回任何数据,但它仍然会与调用方通信不论操作是成功还是失败:如果操作失败,调用方将得到一个异常。如果方法成功了,但返回结果时与调用方的通信失败了怎么办?例如:

If LogVisit is part of a REST service and there is a network outage; or  如果 LogVisit 是 REST 服务的一部分,并且发生了网络中断; 或者 If both LogVisit and the caller are running in the same process, but the process fails before the caller gets to track successful execution of the LogVisit action?  如果 LogVisi 和调用方在同一个进程中运行,但是在成功地执行LogVisit操作后,在返回调用方之前进程失败了?

In both cases, the consumer will assume failure and try calling LogVisit again. Executing the LogVisit logic again will result in an incorrect increase of the counter’s value. Overall, it will be increased by 2 instead of 1. As in the previous two examples, the code fails to implement the transaction script pattern correctly, and inadvertently leads to corrupting the system’s state.

在这两种情况下,使用者都会假设失败,然后再次尝试调用 Logvisite。再次执行LogVisit 逻辑将导致计数器值的不正确增加。总之,它将增加2,而不是1。与前两个示例一样,代码无法正确实现事务脚本模式,并且无意中导致损坏系统状态。

As in the previous example, there is no simple fix for this issue. It all depends on the business domain and its needs. In this specific example, one way to ensure transactional behavior is to make the operation idempotent: that is, leading to the same result even if the operation repeated multiple times.

与前面的示例一样,这个问题没有简单的修复方法。这完全取决于业务领域及其需求。在这个特定的示例中,确保事务行为的一种方法是使操作为幂等的: 也就是说,即使操作重复多次,也会导致相同的结果。

For example, we can ask the consumer to pass the value of the counter. To supply the counter’s value, the caller will have to read the current value first, increase it locally, and then provide the updated value as a parameter. Even if the operation will be executed multiple times, it won’t change the end result:

例如,我们可以要求使用者传递计数器的值。要提供计数器的值,调用方必须首先读取当前值,在本地增加它,然后将更新后的值作为参数提供。即使这个操作会执行多次,它也不会改变最终结果:

1 public class LogVisit 2 { 3 ... 4 5 public void Execute(Guid userId, long visits) 6 { 7 _db.Execute("UPDATE Users SET visits = @p1 WHERE 8 user_id=@p2", 9 visits, userId); 10 } 11 }

Another way to address such an issue is to use optimistic concurrency control: prior to calling the LogVisit operation, the caller has read the counter’s current value and passed it to LogVisit as a parameter. LogVisit will update the counter’s value only if it equals the one initially read by the caller:

解决这个问题的另一种方法是使用乐观并发控制: 在调用 Logvision操作之前,调用者已经读取了计数器的当前值,并将其作为参数传递给 Logvision。只有当计数器的值与调用者最初读取的值相等时,LogVisi 才会更新计数器的值:

1 public class LogVisit 2 { 3 ... 4 5 public void Execute(Guid userId, long expectedVisits) 6 { 7 _db.Execute(@"UPDATE Users SET visits=visits+1 8 WHERE user_id=@p1 and visits = @p2", 9 userId, visits); 10 } 11 }

Subsequent executions of LogVisit with the same input parameters won’t change the data, as the WHERE...visits = @prm2 condition won’t be fulfilled.

使用相同的输入参数执行后续的 Logvisite 不会更改数据,因为 WHERE... views =@prm2条件不会得到满足。

When to Use Transaction Script 何时使用事务脚本

The transaction script pattern is well adapted to the most straightforward problem domains in which the business logic resembles simple procedural operations. For example, in extract-transform-load (ETL) operations, each operation extracts data from a source, applies transformation logic to convert it into another form, and loads the result into the destination store. This process is shown in Figure 5-3.

事务脚本模式很好地适应了最直接的问题领域,其中业务逻辑类似于简单的程序(流程)操作。例如,在提取-转换-装载(ETL)操作中,每个操作从源中提取数据,应用转换逻辑将其转换为另一种形式,并将结果装载到目标存储中。这个过程如图5-3所示。

Figure 5-3. Extract-transform-load data flow

图5-3。提取-转换-装载数据流

The transaction script pattern naturally fits supporting subdomains where, by definition, the business logic is simple. It can also be used as an adapter for integration with external systems—for example, generic subdomains, or as a part of an anticorruption layer (more on that in Chapter 9).

事务脚本模式天然适合支撑子域,根据定义,其中的业务逻辑是简单的。它还可以用作与外部系统集成的适配器——例如,通用子域,或者作为防腐层的一部分(在第9章中有更多介绍)。

The main advantage of the transaction script pattern is its simplicity. It introduces minimal abstractions and minimizes the overhead both in runtime performance and in understanding the business logic. That said, this simplicity is also the pattern’s disadvantage. The more complex the business logic gets, the more it’s prone to duplicate business logic across transactions, and consequently, to result in inconsistent behavior—when the duplicated code goes out of sync. As a result, transaction script should never be used for core subdomains, as this pattern won’t cope with the high complexity of a core subdomain’s business logic.

事务脚本模式的主要优点是其简单性。它引入了最小抽象,并最小化了运行时性能和理解业务逻辑方面的开销。也就是说,这种简单性也是该模式的缺点。业务逻辑越复杂,就越容易在事务之间重复业务逻辑,从而导致不一致的行为——当重复的代码不同步时。因此,事务脚本不应该用于核心子域,因为这种模式无法处理核心子域业务逻辑的高度复杂性。

This simplicity earned the transaction script a dubious reputation. Sometimes the pattern is even treated as an antipattern. After all, if complex business logic is implemented as a transaction script, sooner rather than later it’s going to turn into an unmaintainable, big ball of mud. It should be noted, however, that despite the simplicity, the transaction script pattern is ubiquitous in software development. All the business logic implementation patterns that we will discuss in this and the following chapters, in one way or another, are based on the transaction script pattern.

这种简单性为事务脚本赢得了可疑的声誉,有时甚至将事务脚本模式视为反模式。毕竟,如果将复杂的业务逻辑作为事务脚本实现,那么它很快就会变成一个无法维护的大泥球。然而,应该注意的是,尽管事务脚本模式很简单,但是它在软件开发中是普遍存在的。我们将在本章和后续章节中讨论的所有业务逻辑实现模式都以某种方式基于事务脚本模式。

Active Record 活动记录

An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data. —Martin Fowler

封装数据库表或视图中的行、封装数据库访问并在该数据上添加领域逻辑的对象。——Martin Fowler

Like the transaction script pattern, active record supports cases where the business logic is simple. Here, however, the business logic may operate on more complex data structures. For example, instead of flat records, we can have more complicated object trees and hierarchies, as shown in Figure 5-4.

与事务脚本模式一样,活动记录支持业务逻辑简单的情况。然而,在这里(活动记录模式下),业务逻辑可能操作更复杂的数据结构。例如,我们可以使用更复杂的对象树和层次结构来代替平面记录,如图5-4所示。

Figure 5-4. A more complicated data model with one-to-many and many-to-many relationships

图5-4.  一个具有一对多和多对多关系的更复杂的数据模型

Operating on such data structures via a simple transaction script would result in lots of repetitive code. The mapping of the data to an in-memory representation would be duplicated all over.

通过一个简单的事务脚本操作这样的数据结构会导致大量重复的代码。数据到内存中表示的映射将在所有地方重复。

Implementation 实现

Consequently, this pattern uses dedicated objects, known as active records, to represent complicated data structures. Apart from the data structure, these objects also implement data access methods for creating, reading, updating, and deleting records—the so-called CRUD operations. As a result, the active record objects are coupled to an object-relational mapping (ORM) or some other data access framework. The pattern’s name is derived from the fact that each data structure is “active”; that is, it implements data access logic.

因此,此模式使用专用对象(称为活动记录)来表示复杂的数据结构。除了数据结构之外,这些对象还实现用于创建、读取、更新和删除记录的数据访问方法,即所谓的 CRUD 操作。因此,活动记录对象被耦合到一个对象关系映射(ORM)或其他数据访问框架中。模式的名称来源于每个数据结构都是“活动的”这一事实; 也就是说,它实现了数据访问逻辑。

As in the previous pattern, the system’s business logic is organized in a transaction script. The difference between the two patterns is that in this case, instead of accessing the database directly, the transaction script manipulates active record objects. When it completes, the operation has to either complete or fail as an atomic transaction:

与前面的模式一样,系统的业务逻辑组织在一个事务脚本中。这两种模式的区别在于,在这种情况下,事务脚本不直接访问数据库,而是操作活动记录对象。当它完成时,操作必须以原子事务的形式完成或失败:

1 public class CreateUser 2 { 3 ... 4 public void Execute(userDetails) 5 { 6 try 7 { 8 _db.StartTransaction(); 9 var user = new User(); 10 user.Name = userDetails.Name; 11 user.Email = userDetails.Email; 12 user.Save(); 13 _db.Commit(); 14 } catch { 15 _db.Rollback(); 16 throw; 17 } 18 } 19 }

The pattern’s goal is to encapsulate the complexity of mapping the inmemory object to the database’s schema. In addition to being responsible for persistence, the active record objects can contain business logic; for example, validating new values assigned to the fields, or even implementing business-related procedures that manipulate an object’s data. That said, the distinctive feature of an active record object is the separation of data structures and behavior (business logic). Usually, an active record’s fields have public getters and setters that allow external procedures to modify its state.

该模式的目标是封装将内存对象映射到数据库模式的复杂性。除了负责持久化之外,活动记录对象还可以包含业务逻辑;例如,验证分配给字段的新值,甚至实现处理对象数据的业务相关过程。也就是说,活动记录对象的独特特征是数据结构和行为(业务逻辑)的分离。通常,活动记录的字段具有公共 getter 和 setter,允许外部过程修改其状态。

When to Use Active Record 何时使用活动记录

Because an active record is essentially a transaction script that optimizes access to databases, this pattern can only support relatively simple business logic, such as CRUD operations, which, at most, validate the user’s input.

因为活动记录本质上是一个优化数据库访问的事务脚本,所以这种模式只能支持相对简单的业务逻辑,比如 CRUD 操作,它最多只能验证用户的输入。

Accordingly, as in the case of the transaction script pattern, the active record pattern lends itself to supporting subdomains, integration of external solutions for generic subdomains, or model transformation tasks. The difference between the patterns is that active record addresses the complexity of mapping complicated data structures to a database’s schema.

因此,与事务脚本模式的情况一样,活动记录模式适合支撑子域、集成通用子域的外部解决方案或模型转换任务。模式之间的区别在于,活动记录解决了将复杂数据结构映射到数据库架构(模式)的复杂性。

The active record pattern is also known as an anemic domain model antipattern; in other words, an improperly designed domain model. I prefer to restrain from the negative connotation of the words anemic and antipattern. This pattern is a tool. Like any tool, it can solve problems, but it can potentially introduce more harm than good when applied in the wrong context. There is nothing wrong with using active records when the business logic is simple. Furthermore, using a more elaborate pattern when implementing simple business logic will also result in harm by introducing accidental complexity. In the next chapter, you will learn what a domain model is and how it differs from an active record pattern.

活动记录模式也称为贫血领域模型反模式; 换句话说,就是设计不当的领域模型。我更倾向于规避贫血和反模式这两个词的负面含义,这种模式是一种工具。与任何工具一样,它可以解决问题,但是如果应用在错误的环境中,它可能会带来弊大于利的后果。当业务逻辑很简单时,使用活动记录并没有什么错。此外,在实现简单业务逻辑时使用更复杂的模式也会因引入附带的复杂性而造成损害。在下一章中,您将了解什么是领域模型,以及它与活动记录模式的区别。

NOTE  备注

It’s important to stress that in this context, active record refers to the design pattern, not the Active Record framework. The pattern name was coined in Patterns of Enterprise Application Architecture by Martin Fowler. The framework came later as one way to implement the pattern. In our context, we are talking about the design pattern and the concepts behind it, not a specific implementation.

需要强调的是,在此语境中,活动记录指的是设计模式,而不是活动记录框架。模式名称是 Martin Fowler 在企业应用程序架构模式中创造的。活动记录框架后来成为实现该模式的一种方法。在我们的语境中,我们讨论的是设计模式及其背后的概念,而不是具体的实现。

Be Pragmatic 务实

Although business data is important and the code we design and build should protect its integrity, there are cases in which a pragmatic approach is more desirable.

尽管业务数据很重要,而且我们设计和构建的代码应该保护其完整性,但在某些情况下,实用的方法更为可取。

Especially at high levels of scale, there are cases when data consistency guarantees can be relaxed. Check whether corrupting the state of one record out of 1 million is really a showstopper for the business and whether it can negatively affect the performance and profitability of the business. For example, let’s assume you are building a system that ingests billions of events per day from IoT devices. Is it a big deal if 0.001% of the events will be duplicated or lost?

特别是在高规模级别,有些情况下可以放宽数据一致性的保证。检查100万分之一的记录被破坏是否真的会阻碍业务的发展,以及是否会对业务的表现和盈利能力产生负面影响。例如,让我们假设您正在构建一个每天从物联网设备摄取数十亿事件的系统。如果0.001% 的事件将被重复或丢失,这是一个大问题吗?

As always, there are no universal laws. It all depends on the business domain you are working in. It’s OK to “cut corners” where possible; just make sure you evaluate the risks and business implications.

和往常一样,没有普遍的规则,这完全取决于你工作的业务领域。在可能的情况下“走捷径”是可以的;只要确保您评估了风险和业务影响。

Conclusion 总结

In this chapter, we covered two patterns for implementing business logic:

在本章中,我们介绍了实现业务逻辑的两种模式:

Transaction script  事务脚本

       This pattern organizes the system’s operations as simple, straightforward procedural scripts. The procedures ensure that each operation is transactional—either it succeeds or it fails. The transaction script pattern lends itself to supporting subdomains, with business logic resembling simple, ETL-like operations.  此模式将系统操作组织为简单、直观的程序(流程)脚本。这些程序(流程)确保每个操作都是事务性的——要么成功,要么失败。事务脚本模式适用于支撑子域,业务逻辑类似于简单的、类似 ETL 的操作。

Active record  活动记录

       When the business logic is simple but operates on complicated data structures, you can implement those data structures as active records. An active record object is a data structure that provides simple CRUD data access methods.  当业务逻辑很简单但是操作复杂的数据结构时,您可以将这些数据结构实现为活动记录。活动记录对象是提供简单 CRUD 数据访问方法的数据结构。

The two patterns discussed in this chapter are oriented toward cases of rather simple business logic. In the next chapter, we will turn to more complex business logic and discuss how to tackle the complexity using the domain model pattern.

本章中讨论的两种模式面向相当简单的业务逻辑情况。在下一章中,我们将转向更复杂的业务逻辑,并讨论如何使用领域模型模式来处理这种复杂性。

Exercises 练习

1. Which of the discussed patterns should be used for implementing a core subdomain’s business logic?  应该使用哪种讨论过的模式来实现核心子域的业务逻辑?

a. Transaction script.  事务脚本

b. Active record.  活动记录

c. Neither of these patterns can be used to implement a core subdomain.  这两种模式都不能用于实现核心子域。

d. Both can be used to implement a core subdomain.  两者都可以用来实现核心子域。

2. Consider the following code:  思考以下代码:

1 public void CreateTicket(TicketData data) 2 { 3 var agent = FindLeastBusyAgent(); 4 agent.ActiveTickets = agent.ActiveTickets + 1; 5 agent.Save(); 6 var ticket = new Ticket(); 7 ticket.Id = Guid.New(); 8 ticket.Data = data; 9 ticket.AssignedAgent = agent; 10 ticket.Save(); 11 _alerts.Send(agent, "You have a new ticket!"); 12 }

Assuming there is no high-level transaction mechanism, what potential data consistency issues can you spot here?

假设没有高级事务机制,您在这里可以发现哪些潜在的数据一致性问题?

a. On receiving a new ticket, the assigned agent’s counter of active tickets can be increased by more than 1.  当收到一张新票时,指定代理人的有效票计数器可以增加超过1。

b. An agent’s counter of active tickets can be increased by 1 but the agent won’t get assigned any new tickets.  一个代理人的有效票计数器可以增加1,但是该代理人不会得到任何新的票。

c. An agent can get a new ticket but won’t be notified about it.  代理人可以得到一张新票,但不会被通知。

d. All of the above issues are possible.  以上所有问题都是可能的。

3. In the preceding code, there is at least one more possible edge case that can corrupt the system’s state. Can you spot it?  在前面的代码中,至少还有一种可能的边缘情况会破坏系统的状态。你能看出来吗?

4. Going back to the example of WolfDesk in the book’s Preface, what parts of the system could potentially be implemented as a transaction script or an active record?  回到本书前言中的 WolfDesk 示例,系统的哪些部分可以作为事务脚本或活动记录实现?

 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有