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

C# 为个人/客户建模联系人详细信息,c#,domain-driven-design,software-design,C#,Domain Driven Design,Software Design,我想知道是否有一种更优雅的方式来管理个人的联系方式。暂时忘掉SQL方面的事情吧,我很好奇人们可能会如何尝试通过DDD方法来驱动它 为了让DDD作为一个整体使用起来更舒服,我在一些代码上胡闹,并得出了以下看起来很糟糕的结论 首先,我有一个名为Person的对象(为了本文的目的而简化),在这里我设想了一些方法来添加和基本上管理不同的个人交流方法 public class Person { public Person() { this.ContactDetails =

我想知道是否有一种更优雅的方式来管理个人的联系方式。暂时忘掉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; }
}
但后来我把自己置身于这种方法的困境中,因为尽管它对电子邮件和电话等琐碎的东西很有效,但当涉及到像邮政这样的东西时,字符串并不能完全切断它。因此,在此之后,我将采用一种方法,让每种通信机制都以其自己的类型表示,即:

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; }
}
然后,每个类型都可以表示为Person类中的集合或单个实例?似乎冗长,但可能更可读和可维护

我想问题是,是否有一种更优雅的方式来实现这样一个特性,是否有人能为我指出一个类似的好例子。我想这是一个需要克服的常见问题


干杯,DS。

在我学习DDD的过程中,有时我看到的是模式而不是问题。。。一个有趣的例子是我提供的关于菜单的另一个答案,它有不同的类别,比如初学者、主菜、沙漠等等

我将其隐式建模为一个类别字符串。在我发布了第二个答案后,有人建议将其建模为明确的列表:

Menu {
List<Food> starters;
List<Food> entrees;
List<Food> desserts;
List<Food> drinks;
}
或者更好的是,当您更新用户手机时,您可能会触发一个
MobileUpdated
事件,其他地方的一些代码(发送短信的专家,其他什么都没有)正在监听这些事件-对我来说,这是DDD将代码分解到专家系统中的真正力量

总而言之,我认为你的第二个建议是使用
Post
Mobile
Landline
Email
进行显式建模,这是最有意义的


我不会说这是一个DDD域,因为没有足够的关于您需要的任何复杂逻辑(或多用户竞争条件)的信息,请注意,如果在这种情况下编写CRUD应用程序更有意义,您可能会更好。

DDD的核心思想是,领域建模必须通过与领域专家的讨论形成。如果你只是凭空编造这些类名,那么它们很可能与你真正的域并不完全匹配。诸如电子邮件或电话之类的琐事应该是正确的,但对于其他人来说,你可能首先需要专家的反馈


一般来说,与基本类型相比,更倾向于使用专用值对象进行语义丰富的建模,这确实是一个好主意。但在C语言中,这是有代价的,因为所需的样板代码数量巨大(例如,与F语言不同)。这就是为什么我通常只喜欢在类型具有多个属性或存在特定构造规则或不变量时才这样做。

可以做的一件好事是将类型建模为不可变的
值对象。比如:

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

    public string TelephoneNo { get; set; }
}
可能成为:

public class TelephoneNumber
{
    private string areaCode;
    private string subscriberNumber;

    private TelephoneNumber()
    {
    }

    public TelephoneNumber(string areaCode, string subscriberNumber)
    {
        this.AreaCode = areaCode;
        this.SubscriberNumber = subscriberNumber;
    }

    public string AreaCode
    {
        get
        {
            return this.areaCode;
        }

        private set
        {
            if (value == null)
            {
                throw new ArgumentNullException("AreaCode");
            }

            if ((value.Length <= 0) || (value.Length > 5))
            {
                throw new ArgumentOutOfRangeException("AreaCode");
            }

            this.areaCode = value;
        }
    }

    // Etc.
}
公共类电话号码
{
专用字符串区域码;
私有字符串subscriberNumber;
私人电话号码()
{
}
公用电话号码(字符串区号、字符串下标号)
{
this.AreaCode=区域代码;
this.SubscriberNumber=SubscriberNumber;
}
公共字符串区域码
{
得到
{
返回此.areaCode;
}
专用设备
{
如果(值==null)
{
抛出新的ArgumentNullException(“区域代码”);
}
如果((值5)
{
抛出新ArgumentOutOfRangeException(“区域代码”);
}
此.areaCode=值;
}
}
//等等。
}

我们确实知道“电子邮件”、“电话”和“地址”是什么联系方式,因此在确定了这些联系方式之后,我们首先要做的是根据这些联系方式的实际情况对这些概念进行建模。让我们以“电子邮件”为例,看看它到底是什么,以便对其进行正确的建模。它是一个值对象(一个不可变的对象)一旦创建,它将永远不会改变,就像整数也是一个不可变的对象一样。区别在于,对于整数建模,我们可以使用任何编程语言提供的int类型,但问题是我们使用什么类来建模电子邮件?大多数人都会使用字符串实例来建模电子邮件,但实际上是这样的OK?为了回答这个问题,让我们看看String对象知道响应什么协议(消息集):“charAt(一个索引)、replace(aString、另一个字符串)等等。。。“。想象一下,如果我们使用字符串类对电子邮件建模,我们可以要求电子邮件“替换(aString,另一个字符串)”“。这听起来很奇怪,这封邮件不应该是电子邮件向其他对象公开的行为的一部分。同样重要的是,我们说电子邮件是不可更改的,它不能暴露行为,最终改变它的状态。因此,我们需要创建一个全新的抽象来建模电子邮件,这是什么?电子邮件课程终于来了!!!我知道你建议了,但我只是想让你看看为什么我们需要创建一个电子邮件类。 首先,这是DDD(面向对象),所以忘记避免setter和getter。在您创建的电子邮件类中,您公开了一个setter方法,这意味着您可以更改电子邮件,它与电子邮件的性质(不可变)相矛盾。电子邮件从创建之时起即不可更改:

Email.fromString("monicalewinsky@gmail.com");
这和做同样的事

new Email("monicalewinsky@gmail.com");
fromString方法是一个工厂方法,它添加了
Email.fromString("monicalewinsky@gmail.com");
new Email("monicalewinsky@gmail.com");
Email(String anEmailStringRepresentation) {
    assertIsValid(anEmailStringRepresentation);
}
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)
}
ContactMethod.for(Email.fromString("monica@gmail.com"));
ContactMethod.for(PhoneNumber("34234234234"));
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());
 }
}
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));
    }
}