1

I'm creating an application which's architecture is based on Uncle Bob's Clean Architecture concepts and DDD. Note that it is BASED on DDD, so I gave myself the freedom to differ from strict DDD.

To create this application, I am using C# with .Net Standard 2.0

One of the principles of DDD relates to Value Objects. The definition for Value Objects, according to Wikipedia is as follows:

Value Object

An object that contains attributes but has no conceptual identity. They should be treated as immutable.

Example: When people exchange business cards, they generally do not distinguish between each unique card; they only are concerned about the information printed on the card. In this context, business cards are value object

Now, I want that my Value Objects does not allow their creation if some validation does not succeed. When it happens, an exception would be thrown during the instantiation. I really meant to throw an exception there, because the core of the architecture really does not expect any invalid data to reach that point.

Before going further on this question, to give you guys some more background, here is my architecture (NOTE: still incomplete):

My Clean Architecture Proposal

The rules I am following in this architecture is:

  1. A layer can only know about its immediate innermost neighbor layer's interfaces
  2. A layer cannot know anything about any outermost layer
  3. All communications between layers MUST be done through interfaces
  4. Each layer must be independently deployable
  5. Each layer must be independently developable

To better understand the arrows in this diagram, I recommend reading those Stack Exchanges's questions:

https://stackoverflow.com/questions/1874049/explanation-of-the-uml-arrows

Aggregation vs Composition

Now, the challenge I'm facing right now, is finding a good way to use the validators. I'm not satisfied with my architecture in this point. The problem is the following:

Since I can have thousands of Value Objects being instantiated at a given time, I don't want each instance of the Value Objects to have an instance method to perform the validation. I want the validation method to be static, since it's logic will be the same for every instance. Also, I want the validation logic to be available for the upper layer of the architecture to use to perform validations without trying to instantiating the Value Objects, thus causing an expensive exception to be thrown.

The problem is: C# DOES NOT ALLOW polymorphism with static methods, so I can't do something like:

internal interface IValueObject<T>
{
    T Value { get; }
    static bool IsValid(T value);
}

How can I achieve this functionality without relying on static methods polymorphism and, at the same time, not wasting memory?

  • 2
    One thing which is a little unclear to me - what's the problem in having your validation as a non-static method? – Ben Cottrell Mar 15 '18 at 20:00
  • 6
    @AndréMarcondesTeixeira That's not how methods work - methods aren't objects - a non-static method uses the same amount of memory in an object's "footprint" as a static method (i.e. none whatsoever). – Ben Cottrell Mar 15 '18 at 20:03
  • 7
    @AndréMarcondesTeixeira No, instances do not have any "copies" of any methods. The method belongs to the class, not the instance. static/non-static doesn't make any difference whatsoever. If you're interested in reading about how this works under-the-hood, have a read here: blog.csdn.net/phiger/article/details/5566988 – Ben Cottrell Mar 15 '18 at 20:05
  • 3
    You are worried about the memory footprint of the validator enough to need it to be static, but are also relying on expensive exception-throwing as your basic validation scheme? I bet the exceptions will cost you more in performance that having a lot of validator classes instantiated. However you probably need to actually measure this before assuming much else. – Graham Mar 15 '18 at 21:11
  • 1
    @Fabio That's not what 'Value Object' means in DDD - martinfowler.com/bliki/ValueObject.html – Ben Cottrell Mar 16 '18 at 6:58
3

The answer to your question is just to put the logic in the constructor. Using a static vs instance method will not effect the performance in the way you think it will, and including methods like isValid is nearly always a sign of an anemic model. But there is a bigger issue at play here:

I understand the temptation to validate the attributes of each Value Object on construction in order to throw exceptions/catch invariants as soon as possible, but that can lead to all sorts of other issues as your system grows because validation is almost always a product of context (of which a constructor has none). It also affords a certain tendency to create superficial invariants. The best place to enforce rules regarding some piece of data is often at the point of use for that data. This allows the context to play a role in determining what invariants exist.

For example, rather than enforcing an invariant regarding the data allowed on a BusinessCard during construction (say no SwearWords or something), enforce the invariant upon the transfer of that BusinessCard from a SalesAssociate to a Customer. By defering the enforcement (the decision) to be as late as possible (coupled to the use-case), you are able to use the context to help guide the decision (you have more data at your disposal).

Of course, this isn't a law of DDD (or any system) - there may be no conceivable use-case for startDate to ever be greater than endDate for example - but this idea of "deferment" is a cornerstone of good architecture. When you find yourself repeating a check for SwearWords in some other transaction, you will have been given a valuable opportunity: a chance to do some knowledge crunching and possibly refactor your model towards deeper insight. Is there some thread or idea that binds the transfer of a BusinessCard with this new transaction? Can it be extracted and made more explicit? If not, at the very worst we are talking about the invocation of a static (or private) method in two places here to perform the actual check.

Now, for my silly example there may be absolutely no reason for a BusinessCard to include a SwearWord and, in the end, put the enforcement of this invariant in the constructor to avoid any possible duplication, but I can guarantee that this will not always be the case. Furthermore, it's far easier to refactor the enforcement "upward" (to apply to all BusinessCards in construction) than finding, at some later point, a reason why it needs to be removed and pushing it "downward" where you will be forced to hunt down each use-case individually and "re-apply" the rule, or worse, figure out some sort of special work-around.

The quality of the design of a system is not all at about how well it works (there are plenty of poorly designed systems operating just fine), rather how easily it can be changed. When your goal is to design a system with a flexible, maintainable, develop-able, deploy-able architecture, an easy rule of thumb to follow - that I find tends to lead developers in the right direction - is to defer as many decisions as possible until absolutely necessary.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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