领域对象属性和封装
我有一个具有两个属性的电子邮件对象:标签和值。系统用户需要验证其电子邮件才能在系统中使用。验证过程非常简单:
- 为电子邮件设置激活码
- 发送一封包含激活码的电子邮件以验证电子邮件是否有效
电子邮件对象如下所示:
class Email {
String label
String value
EmailStatus status
String activationCode
boolean requestVerification() {
// Set the activationCode that will be refereced to change the email status
this.activationCode = UUID
// Raise an event to send a notification email by a communication service
EventManager.fire('emailVerificationRequest',this)
}
一切正常,除了activationCode属性感觉不正确在电子邮件对象内。它不以任何方式描述对象的状态,并且仅在电子邮件验证过程中使用。因此,我修改了代码以引入一个名为 ActivationToken 的新对象。该对象将用于封装activationCode。这是电子邮件对象的新版本,
class Email {
String label
String value
EmailStatus status
boolean requestVerification() {
new ActivationToken(target:this,code:UUID,expiryDate:new Date()).save()
// Raise an event to send a notification email by a communication service
EventManager.fire('emailVerificationRequest',this)
}
class ActivationToken {
Email target
String code
Date expiryDate
}
- 这是一个合理的域设计,还是我让我的对象变得毫无意义?
- requestVerification 方法是否首先属于电子邮件对象,还是应该放在其他地方?在服务中或在流程中。
- 是否有任何设计模式可以遵循来解决类似的问题
更新
我想解释为什么即使在第二次重构方法之后我仍将 requestVerfication 方法保留为电子邮件域对象的一部分。
我有一个远程接口,它通过调度程序以以下方式直接与域对象交互:
remote://email/6/do/requestVerification
最初,我想保持通过域对象与后端进行的所有通信,如果需要交互,例如与服务交互,我正在使用IOC将其注入域对象并使用域对象作为代理。远程接口和域对象之间的分离看起来很干净,但事实证明这是一个非常糟糕的主意,因为它破坏了域设计并引入了对与行为或行为无关的外部对象(在本例中为 EmailVerificationService )的无用依赖。域对象的状态方面
解决此问题的另一个解决方案可能是将 requestVerification 方法保留在它自然所属的服务中,并向通信协议引入新的语法,例如:
remote://service/email/do/requestVerification
你们觉得怎么样?
谢谢你
-肯
I have an Email object with two properties, label and value. System user need to verify their email before they can use it in the system. The verification process is very simple:
- Set an activation code for the email
- Send an email with the activation code to verify that the email is valid
The email object looks like the following:
class Email {
String label
String value
EmailStatus status
String activationCode
boolean requestVerification() {
// Set the activationCode that will be refereced to change the email status
this.activationCode = UUID
// Raise an event to send a notification email by a communication service
EventManager.fire('emailVerificationRequest',this)
}
All works fine, except that the activationCode property doesn't feel right within the Email object. It doesn't describe in anyway the state of the object and it's only used in the email validation process. So I modified my code to introduce a new object called ActivationToken. The object will be used to encapsulate the activationCode. Here's the new version of the email object
class Email {
String label
String value
EmailStatus status
boolean requestVerification() {
new ActivationToken(target:this,code:UUID,expiryDate:new Date()).save()
// Raise an event to send a notification email by a communication service
EventManager.fire('emailVerificationRequest',this)
}
class ActivationToken {
Email target
String code
Date expiryDate
}
- Is this a sound Domain Design or am I complicating my object for nothing
- Does the requestVerification method belong to the Email object in the first place or should it be placed elsewhere; in a service, or within a process.
- Is there any design pattern that I can follow to tackle similar problems
Update
I wanted to explain why i kept the requestVerfication method part of the Email domain object even after the second refactoring approach.
I have a remote interface that interacts directly with domain objects through a dispatcher in the following fashion:
remote://email/6/do/requestVerification
Originally i wanted to keep all the communication with the backend channeled through the domain objects and if there is a need to interact, say, with a service i was using IOC to inject it into the domain object and use the domain object as a proxy. The separation between the remote interface and the domain object looked clean but that turned out to be a very bad idea as it beaks the domain design and introduces useless dependency to external object (in this case EmailVerificationService ) that has nothing to do with the behavior or the state aspects of the domain object
The other solution to solve this could be to keep the requestVerification method in a service where it naturally belongs and introduce a new syntax to the communication protocol such as:
remote://service/email/do/requestVerification
What do you guys think ?
Thank you
-ken
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
就我个人而言,我认为这两种方法都可以接受,但更喜欢第二种。我将使用领域专家的输入来确定哪种方法。在对域进行建模时,激活代码是否明确属于要求的一部分,或者您是否有理由在电子邮件帐户验证后维护它?如果您没有明确的理由采用第一种方法,我会坚持第二种方法。
关于您的个人问题:
我不会认为它使您的对象变得复杂,更多的是从域中抽象出服务级别问题。是的,它需要更多代码和更多工作,但这可能是更好的方法 - 除非您有明确的理由使用原始方法。
正如所提到的,我确实认为这是服务级别的责任,并且应该在某种 EmailVerificationService 中。在域模型本身中,您只关心电子邮件是否有效,而不关心验证电子邮件的方式。
在我看来,您已经在尽可能使用最好的方法了。包含从域对象引发的事件的事件总线是干净且可靠的。
Personally, I find both approaches to be acceptable, but would prefer the second. What I would use to determine which approach, would be the input of the domain expert. When modelling the domain, was the activation code something explicitly part of the requirement, or do you have a reason to maintain it after the email account have been validated? If you do not have an explicit reason to go with your first approach, I would stick with your second.
In regards to your individual questions:
I wouldn't go as far as to consider it being complicating your object, more of abstracting service-level concerns from the domain. Yes, it is more code, and more work, but is is probably the better approach - unless you have an explicit reason to use the original approach.
As alluded to, I really think that this is a service-level responsibility, and should be in some sort of EmailVerificationService. In the domain model itself, you only really care if the email is valid, not the means by which it is validated.
You are already using the best approach that you can, in my opinion. An eventing bus with events raised from the domain object is clean and reliable.
您已采取正确的步骤将 Email 与 ActivationToken 分开 - 从概念上讲,它们是独立的东西(顺便说一句,为了清楚起见,我会使用 EmailActivationToken )。
通常,EmailVerificationService 将用于封装验证逻辑。
You've taken the right steps in separating Email from ActivationToken - conceptually they're separate things (btw, I would have used EmailActivationToken for clarity).
Typically, an EmailVerificationService would be used to encapsulate the verification logic.
我认为将功能封装在 ActivationToken 中是个好主意。但是,通过在
Email
类中初始化ActivationToken
,您已经创建了对外部资源的隐藏依赖项。这实际上使得Email
难以进行单元测试,也难以被具有其他激活方案的其他客户端重用。相反,您可能需要使用 依赖注入/控制反转 模式来注入
ActivationToken
到Email
类中。这允许您注入不同的激活策略,并为Email
类的简单单元测试打开大门。为此,您需要一个
ActivationToken
接口,并且Email
类的构造函数应引用实现此接口的对象:现在是
Email
code> 类对外部资源没有隐藏的依赖关系。I think it is a good idea to encapsulate the functionality in
ActivationToken
. But by initializingActivationToken
within theEmail
class you have created a hidden dependency on an external resource. This effectively makes theEmail
hard to unit test and to reuse by other clients that have another scheme for activation.Instead you may want to use the Dependency Injection/Inversion of Control pattern to inject the
ActivationToken
into theEmail
class. This allows you to inject different activation strategies and opens up for easy unit testing of theEmail
class.To do this you'd need an interface for
ActivationToken
and the constructor of theEmail
class should take reference to an object implementing this interface:Now the
Email
class have no hidden dependencies on external resources.