1

I work on a system that utilizes a form of n-tier architecture in that we have data and service models. The service models derive from the data models:

public class FooDataModel {
    public int FooProperty { get; set; }
}
public sealed class Foo : FooDataModel {
    public bool Destroy => FooProperty < 0;
}

A lot of our data models are structured and as such contain references to other data models:

public class BarDataModel {
    public FooDataModel FooData { get; set; }
}
public class Bar : BarDataModel {
    public bool Create => FooData.FooProperty > 0;
}

The obvious issue here is that if I use Bar, my FooData property isn't of type Foo it's of type FooDataModel. This is something I want to avoid heavily so that only the service models are used. My reasoning is that these data and service models are defined based on APIs offered by real world clients. As such, I want to define their data models as 100% accurate representations of the models the respective clients provide.

It's entirely possible that company A can (and will) define a similar model to what is also defined by company B. For example:

public class Cat { public int NumberOfWhiskers { get; set; } } // Company A
public class Cat { public int WhiskerCount { get; set; } }     // Company B

At the data level, there's no need to distinguish that this is the model defined for company A or B. Instead that's handled at the service level by naming the models CompanyAyyCat and CompanyBeeCat to prevent ambiguity because more often than not, later processing involves models from both companies at the same time.

My first thought to remedy this is to utilize interfaces, BUT, we want the data model assemblies to be self sufficient and wouldn't want to create an interface to also represent every type we nest, as that seems like overkill for the situation.

I know we can also hide the properties using the new keyword:

public class Bar : BarDataModel {
    public bool Create => FooData.FooProperty > 0;
    public new Foo FooData { get; set; }
}

But, this feels a bit dirty/abusive/hackish in that I believe it should be easier to accomplish this with some changes to the design.

Note: Future readers should keep in mind that hiding the underlying property in Bar causes issues with XML serialization without adding attributes.


What options are available to ensure only service models are used as properties in service models that derive from data models (i.e. Bar.FooData is of type Foo instead of FooDataModel)? Are there any alternatives to my current approach?

4
  • Is each of the classes FooDataModel, Foo, BarDataModel and Bar in a different assembly? Or do you have one assembly for the data models, and one for the service classes? Or one for all "Foo..." classes and one for all "Bar..." classes?
    – Doc Brown
    Commented Aug 18, 2021 at 20:01
  • @DocBrown data models and service models are contained in their own respective assemblies, in the form of MyCompany.Data.ClientCompany and MyCompany.Services.ClientCompany. So each client gets their own data and service assemblies. Commented Aug 18, 2021 at 20:27
  • So to ensure clarity, Foo and Bar reside in one assembly, while FooDataModel and BarDataModel reside in another. Commented Aug 18, 2021 at 20:28
  • In addition to what Doc Brown suggested (composition over inheritance), I think it's worth considering that there is no fundamental reason to have 1-to-1 mapping between your data models and your service models. Think about what your services do and about the flow of data through your application, then based on that, create service models (possibly instantiated on demand) to suit the needs of the services themselves - if that means that one service model wraps a couple of data models, or that two different service models wrap the same data model for different use cases, then so be it. Commented Aug 19, 2021 at 3:44

1 Answer 1

6

I think the fundamental flaw in this design is the usage of inheritance. A Foo object is not a FooDataModel object, but it is implemented in terms of FooDataModel object. In C++, one could use protected inheritance for this, but C# does not have this concept.

To solve this, favor composition over inheritance - make Foo have a private member of type FooDataModel, and Bar a private member of type BarDataModel. That will allow you to implement service classes Foo and Bar with public properties from the service layer exclusively.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.