0

I am trying to implement DDD along with Hexagonal Architecture. One of the things that I am struggling with is validations. I had explored a lot of articles on where the validation should be but it seems there is no one way. I would like to find the most appropriate way to implement in my scenario. Here is the scenario.

  • I have an HTML page which contains few fields like Title, Date, Assignee.
  • This HTML page has basic validation for empty fields. Once the form is submitted it calls one of the rest services.
  • The job of Rest Controller is just to send the data sent from the browser to one of the services.
  • So the method in the service class will accept that input. I'm not trusting in the caller. I want to validate the input before creating any domain object.
  • Here an example of the validations I need to do

    • The title should not be empty
    • Title length should be less than X characters (The value of X is configurable and retrieved from the database)
    • The assignment should be from a predefined set of available assignments(Retrieved from another application or DB)

The solution implemented (at the moment) starts with an interface

  public interface IValidator<T> {
     public ValidationResult validate(T entity)
  }

Then, I have implemented multiple validators. For instance, TitleValidator, DateValidator and AssignmentValidator.

Since the number of implementations might increase, I have created a wrapper to hold and configure the validators. Finally, the wrapper is injected into the services.

public class MyService {   
   public MyService(WrapperValidator wrapperValidator){

   } 
   public void createData(InputData inputData){

       ValidationResult result = this.wrapperValidator.validate(inputData);
       if(result.Ok()){ 
          //Proceed with domain objects creation and applying business rules. Domain objects will have other validation as well.
       }

   }
}

I retrieve the configuration parameters like max length, predefined set of assignees and sending these settings to the validators from the wrapper.

The WrapperValidator is accessing database and external API to retrieve these configurations.

Is this the right way or should every validator retrieve its configuration by itself?

Any suggestions, improvements or guidance from more experienced developers is appreciated.

0

You should probably review the ideas behind Parse, Don't Validate....

Is this the right way or should every validator retrieve its configuration by itself?

In practice, I don't believe it matters very much.

What you describe here is an attempt to ensure that the data here is consistent with data somewhere else. And, frankly, you can't do that. What you can do is ensure that the data here is consistent with your local copy of data from somewhere else.

There are at least three different logical ideas here - how do you do Useful Thing with the local copy of the data, how do you obtain a local copy of the data, what are you going to do if the process for obtaining a local copy of the data fails/times out?

Notice that only one of these three ideas really cares about database vs web api.

You might need to create multiple validators from the same local copy of data - think data transfer object. Depending on what the real consistency requirements are, that might mean that you share a read through cache when constructing those validators, or not.

My guess is that the most important thing is to pay attention to clean module boundaries. That is to say, when you change your mind in the future about how it should work, what you are going to care about is (a) that the changes are local to some part of your code, and (b) that it is easy to figure out where that code is.

Oh, that, and the direction of your dependencies; see Mark Seemann's talk Async Injection -- your life will be a lot easier if you can decompose the problem into components without awareness of context that are used by the components that are aware of context.

are you suggesting that instead of validator retrieving configuration data for validation, we should inject configuration data (DTO) in to validator constructor ?

Almost - I'm suggesting that if you decompose the problem into logically separable parts, then one of those parts will know about validation using in-memory data, and nothing else.

// This is a "pure function" or closure -- no side effects
// Notice that it doesn't care where its inputs come from.
ValidatedInput validatedDataFrom(
    UnvalidatedInput fromUser,
    Configuration currentConfiguration) {
        // do something interesting with these two in memory representations
}

Of course, you'll want to also be able to handle the cases where the UnvalidatedInput isn't valid -- with this signature, you would have to throw an exception, but you could encode that uncertainty into the method signature

Optional<ValidatedInput> validatedDataFrom( /* ... */ ) {}
Either<ValidatedInput, List<ValidationErrors>> validatedDataFrom( /* ... */ ) {}

why do we need to return ValidatedInput

We don't "need" to, but we probably don't want our consumer code coupled to our validation code (they are different ideas that change for different reasons), and using the type system eliminates a certain class of errors (passing unvalidated data to a function that assumes validated data).

This is another application of the ValueObject pattern from the first book. We're making explicit in the code that validated data and unvalidated data are not the same thing (although the underlying representations may be similar), and leveraging the type system to ensure that the stronger guarantees are met where necessary.

  • Hi, are you suggesting that instead of validator retrieving configuration data for validation, we should inject configuration data (DTO) in to validator constructor ? – Rohit Nov 15 at 11:46
  • Thanks for response. Just a query about ValidatedInput & UnvalidatedInput. We are passing in an UnvalidatedInput in to function which will be validated by retrieving data from it, but why do we need to return ValidatedInput ? Is validatedInput copy of UnvalidatedInput or is it an container for validation errors ? – Rohit Nov 15 at 12:19

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.