Design Advice - Nested Classes

When designing requests which contain a collection of another object, I’ve been creating a nested class to use as the DTO to create the sub-resource. I can’t use the Type as this has properties that are assigned by the system. Will I run into any problems using a nested class (with Add SS reference for example)?

If I un-nest ServiceModel.CreateContact.ContactAddress in the following example I’d have to rename it to stop it “clashing” with the ServiceModel.Types.ContactAddress. I guess it could be CreateContactContactAddress or just CreateContactAddress, but this may make it look like a Request DTO to the consumer.

What is the recommended way to handle this scenario?

Here’s a made-up example to explain what I mean. There would be a constraint to ensure at least one address is provided when creating a contact.

In ServiceModel namespace:

public class CreateContact : IReturn<CreateContractResponse>
{
    public string Name {get; set; }
    public List<ContactAddress> Addresses { get; set; } // ContactAddress is the nested class

    public class ContactAddress
    {
        public string Line1 { get; set; }
    }
}

public class CreateContactResponse
{
    public ContactAddress Result { get; set } // returns ServiceModel.Types.ContactAddress
} 

In ServiceModel.Types namespace:

public class ContactAddress
{
    public string Line1 { get; set; }
    public DateTime? ConfirmedAt { get; set; }
    public string TraceSystem { get; set; } // Set by system when an Address is created
}

I’d avoid nested classs as it doesn’t reflect your Service Contract since they need to be extracted and treated like a first-class Type when it’s projected in your Service Contract.

I’d just rename duplicate Types so they’re unique, why do you have different Types to represent a Contact Address? If it’s because they’re for a different type of Contact or represent a different type of Address than this should be included in the name to indicate why they’re different. Within a service boundary you should be having 1 meaning for each Type, overloading it causes unnecessary confusion and potential runtime issues.

The difference is that one represents the created Type the other represents the fields that can be specified when creating the type. I guess it’s the same reason why I wouldn’t define CreateContact as:

public class CreateContact : IReturn<CreateContactReponse>
{
    public Contact ContactToCreate { get; set; }
}

In the example the TraceSystem should never be provided when creating the contact but a created Contact would always have one as it would have been assigned by the system.

This would leave me with NewContactAdress and ContactAddress Types.

If I just have ContactAddress I’d need to make the Id nullable. Then ignore Id, TraceSource and ConfirmedAt values when creating a contact.

If that’s the difference then I’d name the DTO as either CreateContactAddress or NewContactAddress which indicates why it’s different from ContactAddress, e.g:

public class CreateContact : IReturn<CreateContactResponse>
{
    public string Name {get; set; }
    public List<CreateContactAddress> Addresses { get; set; }
}

1 Like