Modeling contact information for a person / client

I was wondering if there was a more elegant way to manage contact details for an individual. Forget the SQL side of things for a moment, I'm intrigued as to how one could try to manage this with a DDD approach.

I was fooled by some code trying to get comfortable with DDD in general and came up with the following which seems awful.

First, I have an object called Person (simplified for the purposes of this post) where I am looking at methods for adding and essentially managing different ways of communicating with a 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; }
}

      

Two approaches spring. Where I have a pretty simple object like the one below, which is pretty general (using the term loosely).

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; }
    }
}   

      

But then I put myself in a corner with this approach, since while it works well for simple things like email and phone, when it comes to something like mail, the string doesn't quite abbreviate it. So after that, I'm heading towards an approach of making each communication mechanism represent its own type, that is:

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; }
}

      

Can each type be represented as a collection or a single instance of the Person class? It looks like the long winder is arguably more readable and maintainable.

The question, in my opinion, is if there is a more elegant way to implement such a function and can someone point me towards a good example like this. I guess this is a common thing / problem to overcome.

Cheers, DS.

+3


source to share


4 answers


We know exactly what the "email", "phone" and "address" methods of contact are, so once we define them, we must first model these concepts for what they really are. Let's take email as an example. and see what it really is in order to model it correctly. It is a value object (immutable object) that, once created, will never change, just as an integer is also an immutable object. The difference is that to simulate an integer a number we can use the int type provided by any programming language, but the question is, which class are we using to simulate in email? Most people will use a String instance to simulate email,but it normal? To answer it, let's see what protocol (message set) knows the String object to respond to: "charAt (anIndex), replace (aString, anotherString), etc.". Imagine if we were to model email using the String class we could ask for the letter "replace (aString, anotherString)". It sounds strange, this message should not be part of the behavior that email is supposed to expose to other objects. It is also so important that we said that the email is immutable and it cannot reveal behavior that changes its state in the end. So it becomes obvious that we need to create a completely new abstraction for modeling email and what is it? The Email class is finally here! I know you suggested this,but I just wanted to show you why we need the Email class. First of all, it is DDD (Object Oriented), so remember to avoid setters and getters. In the email class you created, you provide a setup method meaning you can change email and this is contrary to the nature of email (immutable). The letter is unchanged from the moment of its creation:

Email.fromString("monicalewinsky@gmail.com");

      

this is the same as doing

new Email("monicalewinsky@gmail.com");

      

The fromString method is a factory method that adds semantics to our domain model. This is very common in smalltalk instead of calling the constructor directly. We all??? It's my pleasure. The email instance must be created as long as it is valid, so the email class must assert that the string it was created from is valid:

Email(String anEmailStringRepresentation) {
    assertIsValid(anEmailStringRepresentation);
}

      

assert is valid, needs to check that it is actually an email string representation. This is something that only has one @ symbol, its local part is valid and then its domain part is valid. You can check wikipedia's email address for a better understanding of how it is put together. Always remember that programming is a learning process because we understand the area better and better, we reflect this area in the code, and it should always correspond to the real world! Our email class should look something like this:

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)
}

      

It. The same thing happens with PhoneNumber. It is also an immutable object and you must create a class with its own protocol. Remember, never use set / get as you showed if we are doing DDD. I don't think you need two value objects, "Phone" and "Mobile", since they are polymorphic objects and you can model a mobile phone number or a home phone number with the Phone number abstraction. It's like simulating a credit card. In the end, you will realize that the CreditCard class is sufficient and better design than several classes such as Visa, MasterCard and so on. Let's skip the Address class and get back to your problem. So far, we have correctly identified and created all the value objects we need.Now we need to create an abstraction to represent email address, phone number, address as contact methods, and if we remain loyal to the domain language, we can say:



ContactMethod.for(Email.fromString("monica@gmail.com"));

      

or

ContactMethod.for(PhoneNumber("34234234234"));

      

etc

so our ContactMethod will look like this:

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());
 }
}

      

Note that ContactMethod is also an immutable class, in fact the rule of thumb is that the Aggregate root should ideally only have a collection of value objects. Finally, your Person class will look like this:

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));
    }
}

      

+5


source


On my journey of learning DDD, sometimes I see patterns instead of problems ... interesting example Everything seems to be a consolidated root - another answer I gave regarding a menu that had different categories like starter, main, desert, etc. .d.

I modeled this implicitly as a category string. After I posted there was a second answer where someone suggested to model them as explicit lists:

Menu {
List<Food> starters;
List<Food> entrees;
List<Food> desserts;
List<Food> drinks;
}

      

So the whole concept of a category for food was removed, it was enlightened to me and saw a different way of modeling and in this case reduced the complexity.

My opinion is to try to simulate the code so that if I sat down with a business expert (who is not a developer) and showed them the high level usage code person.SetMobileNumber("078321411", Countries.UK)

they could figure it out



public void HandleUpdateMobileCommand(UpdateMobileNumber command)
{
    // repositories, services etc are provided in this handler class constructor
    var user = this.UserRepository.GetById(command.UserId);
    user.SetMobile(command.number, command.country);
    this.UserRepository.Save(user);

    // send an SMS, this would get the number from user.Mobile
    this.SmsService.SendThankYouMessage(user);  
}

      

Or even better, you can fire an event MobileUpdated

when you update a custom mobile that is listening to some code elsewhere (which is an expert at sending SMS messages and nothing else) for me this is the real power of DDD to break code into expert systems.

So I think your second sentence of explicit modeling with Post

, Mobile

, Landline

and Email

has the greatest value.

I would not say this DDD domain or not as there is not enough information about any complex logic (or multiplayer race conditions) you require, just to mention, do not forget that you can write a better CRUD application if it makes sense in this situation ...

0


source


There's this central idea in DDD that domain modeling should be shaped through discussion with domain experts. If you compose these class names out of thin air, chances are they won't exactly match your real domain. The trivial ones like email or phone should be correct, but perhaps for others, you need expert feedback.

Generally speaking, it is a good idea to support semantically rich modeling with dedicated value objects over primitive types. In C #, it is expensive because the amount of template code required is huge (unlike F #, for example). This is why I usually prefer to do this only when the type has more than one property, or when there are certain construction rules or invariants to it.

0


source


One good thing you can do is model your types as immutable Value Objects

. So something like:

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

    public string TelephoneNo { get; set; }
}

      

Can be:

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.
}

      

0


source







All Articles