我有一个带有两个属性,标签和值的Email对象。系统用户需要先验证他们的电子邮件,然后才能在系统中使用它。验证过程非常简单:
电子邮件对象如下所示:
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)
}
一切正常,但在Email对象中感觉不到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
}
更新
我想解释一下为什么即使在第二次重构方法之后我也将requestVerfication方法保留为Email域对象的一部分。
我有一个远程接口,它通过调度程序以下列方式直接与域对象进行交互:
remote://email/6/do/requestVerification
最初我想保持与后端通过域对象进行的所有通信,如果需要进行交互,比如说,我正在使用IOC将其注入域对象并使用域对象作为服务代理人。远程接口和域对象之间的分离看起来很干净,但结果却是一个非常糟糕的主意,因为它使域设计失效并将无用的依赖性引入与行为无关的外部对象(在本例中为EmailVerificationService)或域对象的状态方面
解决此问题的另一个解决方案可能是将requestVerification方法保留在自然所属的服务中,并为通信协议引入新语法,例如:
remote://service/email/do/requestVerification
你们有什么想法?
谢谢
-Ken
答案 0 :(得分:1)
就个人而言,我发现这两种方法都可以接受,但更喜欢第二种方法。我将用什么来确定哪种方法,将是域专家的输入。在对域进行建模时,激活码是否明确属于需求的一部分,或者您是否有理由在验证电子邮件帐户后对其进行维护?如果你没有明确的理由采用第一种方法,我会坚持你的第二种方式。
关于您的个人问题:
我不会认为它使您的对象变得复杂,更多的是从域中提取服务级别的问题。是的,这是更多的代码,更多的工作,但可能是更好的方法 - 除非你有明确的理由使用原始方法。
如上所述,我认为这是服务级别的责任,应该在某种EmailVerificationService中。在域模型本身中,您只关心如果电子邮件是有效的,而不是验证它的有效方式。
在我看来,你已经在使用最好的方法了。从域对象引发事件的事件总线干净可靠。
答案 1 :(得分:0)
您已采取正确的步骤将电子邮件与 ActivationToken 分开 - 从概念上讲,它们是分开的东西(顺便说一句,我会使用 EmailActivationToken 为了清楚起见。)
通常, EmailVerificationService 将用于封装验证逻辑。
答案 2 :(得分:0)
我认为将功能封装在ActivationToken
中是个好主意。但是,通过在ActivationToken
类中初始化Email
,您已经创建了对外部资源的隐藏依赖项。这有效地使Email
难以进行单元测试,并被具有另一种激活方案的其他客户端重用。
相反,您可能希望使用Dependency Injection/Inversion of Control模式将ActivationToken
注入Email
类。这允许您注入不同的激活策略并打开,以便轻松对Email
类进行单元测试。
要执行此操作,您需要ActivationToken
的接口,Email
类的构造函数应该引用实现此接口的对象:
interface IActivationToken
{
void Save(object target, string code, DateTime date);
}
class Email
{
IActivationToken token;
// Other properties go here.
public Email(IActivationToken token)
{
this.token = token;
}
boolean RequestVerification()
{
token.Save(this, code, date);
// Raise an event to send a notification email by a communication service
EventManager.fire('emailVerificationRequest', this)
}
}
现在Email
类没有对外部资源的隐藏依赖。