为个人/客户建模联系人详细信息

Dr *_*izo 3 c# domain-driven-design software-design

我想知道是否有一种更优雅的方式来管理个人的联系方式.暂时忘记SQL方面,我很想知道如何通过DDD方法尝试驱动它.

为了让DDD作为一个整体感到舒服,我正在愚弄一些代码并提出以下看起来很糟糕.

首先,我有一个名为Person的对象(为了这个帖子的目的而简化),我设想了添加和实质上管理不同的个人沟通方法的方法.

public class Person
{
    public Person()
    {
        this.ContactDetails = new List<ContactDetails>();
    }

    public void AssociateContactDetails(ContactDetails contactDetails)
    {
        var existingContactDetails = this.ContactDetails.FirstOrDefault(x => x.ContactType == contactDetails.ContactType);

        if (existingContactDetails != null)
        {
            this.ContactDetails.Remove(existingContactDetails);
        }

        this.ContactDetails.Add(contactDetails);
    }

    public IList<ContactDetails> ContactDetails { get; private set; }
}
Run Code Online (Sandbox Code Playgroud)

我想到了两种方法.我有一个相当简单的对象,如下面的那个是非常通用的(使用松散的术语).

public enum ContactType
{
    Email, Telephone, Mobile, Post
}   

public class ContactDetails
{
    private readonly ContactType contactType;
    private readonly string value;

    public ContactDetails(ContactType contactType, string value)
    {
        this.contactType = contactType;
        this.value = value;
    }

    public ContactType ContactType
    {
        get { return this.contactType; }
    }

    public string Value
    {
        get { return this.value; }
    }
}   
Run Code Online (Sandbox Code Playgroud)

但后来我用这种方法把自己放在了一个角落里,虽然它适用于电子邮件和电话这样的小事,但是当涉及像邮政这样的字符串并没有完全削减它.因此,在此之后,我正朝着将每种通信机制用自己的类型表示的方法,即:

public class Post
{
    public Address PostalAddress { get; set; }
}

public class Mobile
{
    public string MobileNo { get; set; }
}

public class Telephone
{
    public string AreaCode { get; set; }

    public string TelephoneNo { get; set; }
}

public class Email
{
    public string EmailAddress { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后,每个类型都可以表示为Person类中的集合或单个实例?似乎长篇大论可能更具可读性和可维护性.

我想的问题是,如果有一种更优雅的方式来实现这样的功能,以及是否有人可以指向一个类似于此的良好示例的方向.我想这是一个需要克服的常见问题.

干杯,DS.

Seb*_*eri 5

我们确切知道联系方式"电子邮件",电话"和"地址"是什么,所以确定我们首先要做的就是根据实际情况对这些概念进行建模.让我们以"电子邮件"为例为了对它进行正确建模,它看看它是什么.它是一个值对象(一个不可变对象),一旦创建它就永远不会改变,因为整数也是一个不可变对象.不同之处在于建模一个整数我们可以使用任何编程语言提供的int类型,但问题是我们用什么类来建模电子邮件?大多数人会使用String实例来建模电子邮件,但是这样可以吗?为了回答它让我们看看String对象知道响应的协议(消息集)是什么:"charAt(anIndex),replace(aString,anotherString)等......".想象一下,如果我们使用String类建模电子邮件我们可以问电子邮件"replace(aString,anotherString)".这听起来很奇怪,那个烂摊子 年龄不应该是电子邮件应该向其他对象公开的行为的一部分.同样如此重要我们说电子邮件是不可变的,它不能暴露最终改变状态的行为.因此,我们需要创建一个全新的抽象来模拟电子邮件,它是什么?电子邮件课最终进来!!! 我知道你建议了,但我只是想让你知道为什么我们需要创建一个Email类.首先这是DDD(面向对象)所以FORGET避免了setter和getter.在您创建的电子邮件类中,您公开了一个setter方法,这意味着您可以更改电子邮件,这与电子邮件的性质(不可变)相矛盾.电子邮件从创建的瞬间不可变:

Email.fromString("monicalewinsky@gmail.com");
Run Code Online (Sandbox Code Playgroud)

这跟做的一样

new Email("monicalewinsky@gmail.com");
Run Code Online (Sandbox Code Playgroud)

fromString方法是一种工厂方法,它为我们的域模型添加语义.这在smalltalk中很常见,而不是直接调用构造函数.我们完了吗???一点也不.应该创建一个电子邮件实例,只要它是有效的,这样电子邮件类就应该声明创建的字符串是有效的:

Email(String anEmailStringRepresentation) {
    assertIsValid(anEmailStringRepresentation);
}
Run Code Online (Sandbox Code Playgroud)

assert有效应该验证它实际上是一个电子邮件字符串表示.这只有一个@字符,其本地部分有效,然后其域部分有效.您可以查看维基百科的电子邮件地址,以更好地了解它的组成方式.永远记住,编程是一个学习过程,只要我们更好地理解一个领域,我们就会在代码中反映出这个领域,并且它总是必须与现实世界保持一致!我们的电子邮件类应该或多或少像:

class Email {

    String value;

    Email(aString) {
        value = aString;
 }

 public String getLocalPart()

 public String getDomainPart()

 public String asString()

 public boolean equals(anObject)

 public static Email fromString(aString)
}
Run Code Online (Sandbox Code Playgroud)

而已.它与PhoneNumber相同.它也是一个不可改变的对象,您应该创建一个具有自己的协议的类.请记住,如果我们正在进行DDD,请不要使用set/get.我认为您不需要两个价值对象电话和移动,因为这些是多态对象,您可以使用TelephoneNumber抽象建模移动电话号码或家庭电话号码.这就像建模信用卡一样.最后,您将最终了解到,如使用Visa,万事达卡等等,可以使用CreditCard类,并且设计更好.让我们跳过Address类,现在让我们回到你的问题.到目前为止,我们已经确定并正确创建了所需的所有值对象.现在我们需要创建一个抽象来表示电子邮件,电话号码,地址作为联系方式,如果我们保持忠诚于域语言,我们可以说:

ContactMethod.for(Email.fromString("monica@gmail.com"));
Run Code Online (Sandbox Code Playgroud)

要么

ContactMethod.for(PhoneNumber("34234234234"));
Run Code Online (Sandbox Code Playgroud)

等等

所以我们的ContactMethod看起来像:

class ContactMethod {

 static EMAIL = 1;
 static PHONE_TYPE = 2;
 static ADDRESS_TYPE = 3;

 String type;

 String value;

 ContactMethod(int aType, String aValue) {
     type = aType;
     value = aValue;
 }

 String getType()

 String getValue()

 public static ContactMethod at(Email anEmail) {
     return new ContactMethod(EMAIL, anEmail.asString());
 }

 public static ContactMethod at(PhoneNumber aPhoneNumber) {
     return new ContactMethod(PHONE_TYPE, aPhoneNumber.asString());
 }

 public static ContactMethod at(Address anAddress) {
     return new ContactMethod(ADDRESS_TYPE, anAddress.asString());
 }
}
Run Code Online (Sandbox Code Playgroud)

看看ContactMethod也是一个不可变类,实际上一个经验法则是聚合根应理想地只有一个值对象的聚合.这最终是你的Person类的样子:

class Person {

    List<ContactMethod> contactMethods;

    contactedAt(Email anEmail) {
        contactMethods.add(ContactMethod.at(anEmail));
    }

    contactedAt(PhoneNumber aPhoneNumber) {
        contactMethods.add(ContactMethod.at(aPhoneNumber));
    }

    contactedAt(Address anAddress) {
        contactMethods.add(ContactMethod.at(anAddress));
    }
}
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你!

如果你喜欢你可以请投票,这样我可以获得更多的声誉,以便能够添加评论!因为目前我不能:)

塞巴斯蒂安.