3

We're working on a new project (backend, API), following the "clean code" and "clean architecture" principles (described in Robert Martin's books), dividing our application into presentation, domain, and data layers. The presentation layer handles routes and data formatting, while the domain layer contains the business logic and accesses data when needed.

We're debating whether data validation should occur in the presentation layer (to validate user input directly) or the domain layer (as part of business logic). I'm convinced it belongs to presentation, since we want to validate raw user input and provide hints what's wrong, and if we do it in domain layer we may be dealing with transformed data that's vastly different from user input, hence we can't give clear hints on what should be fixed. My colleagues however maintain validation is part of business logic and should be part of the domain layer.

By validation I mean checks like "value is integer and in range from 1 to 100" or "string matches this format".

Can anyone clarify which approach is the best and recommend references for further understanding this issue?

3
  • See also softwareengineering.stackexchange.com/a/348916. Or in short, the answer it is part of both.
    – Doc Brown
    Commented Feb 8 at 23:12
  • I would recommend thinking about where you draw the system boundary. On the outside of that boundary, data is untrusted and may be invalid, often in form of DTOs. Internally, you want to rely on the data being valid, often in form of meaningful objects. So that boundary must perform validation+conversion and reject illegal inputs. In web applications, the client cannot be trusted so that boundary is necessarily in the backend, but the part in the backend where validation happens might not yet be the core "business logic". Any frontend validation is just for UX, not for correctness.
    – amon
    Commented Feb 8 at 23:36
  • 1
    Please see this answer
    – John Wu
    Commented Feb 9 at 1:31

4 Answers 4

5

It's an unfortunate fact that the same validation rules often have to be applied and checked repeatedly at different places in the same application.

The validation rules themselves may often be identical, but the reaction to invalid input will not necessarily be the same in each place, and the timing of the enforcement will not necessarily be the same.

For example, as you say, validation rules often have to exist at the presentation layer in order to give immediate feedback to the computer user, and to allow everyday input errors to be corrected. There may be logic that is specific to the presentation layer, such as highlighting erroneous fields, help text which explains acceptable ranges of input, and so on.

However, some kinds of rule may also be inherently enforced at deeper layers. For example, certain fields may have length limits at the storage layer.

For technical reasons, constraints defined at the storage level are often considered the primary or originating constraint, and any duplication of the constraint that exists at the presentation layer is merely an auxiliary or slave version of the same constraint.

What I'm trying to say is that the primary site of enforcement for different constraints can be intrinsically more local to one layer or another, so that it isn't possible to set out with the idea of simply placing all constraints merely in one layer.

However, for reasons of application responsiveness, to ensure that the user is given an opportunity to fix or react to violations as soon as possible, to avoid part-triggering a heavyweight process that will subsequently crash out, or just to try and ensure there is at least one place in source code where a consolidated view of all the constraints is available, it may be considered desirable to have auxiliary and pre-emptory enforcement of constraints at the presentation layer, and the primary constraint at a deeper layer would be triggered only as a backstop when developer error meant the auxiliary/pre-emptory constraint was not properly applied.

And because the hitting of the primary constraint now implies developer error, the programmed reaction of the application may be very different (and involve the alerting of system supervisors) than when only the auxiliary constraints are hit in the presentation layer (which are likely considered normal and lead only to normal user feedback). So even though the criteria for the constraints are the same, the reaction isn't.

Because we now have this concept of primary and auxiliary sites at which constraints are enforced, and because of the possibility of developer error in synchronising the auxiliary constraints with the primary ones, and perhaps for additional architectural reasons, there can often end up being even more than two enforcement sites for the same constraint. There can be a primary site and also multiple auxiliary sites.

And although above I have given an example of the primary site for a constraint being in the storage layer, in some cases, the primary site for a constraint may not even be in the computer system under development, or even originate in the same organisation. For example, the constraint may originate in the law of the land, or in the bureaucratic rules of a different organisation. So even if there was only one site of enforcement in your source code, there may still be additional sites outside the source code in question, and those outside enforcement sites may in fact be the primary enforcement sites which your source code has to reflect (and pre-empt).

For any kind of business information system, it isn't generally possible to avoid having multiple representations of constraints and multiple sites of enforcement in source code, the synchronisation of which has to be handled manually by the developers.

Rather than thinking of this as a wasteful kind of redundancy that can be driven out by better design, consider that each site of enforcement is often encountered at a different time in an overall schedule of processing, and there may be different programmed consequences of a violation being detected at different sites even though the criteria for the violation itself is essentially the same.

3

You're getting sidetracked by thinking of validation as a single indivisible concept and assuming that it must belong to one specific location.

The truth is that different kinds of validation exist, and different validations belong in different places. Just some quick examples:

  • If we want to block people under 18 from renting certain books, that's a business rule, and therefore it belongs to the application layer (or domain, for the purpose of this example domain/application is context-dependent)
  • Did the incoming API request contain correct parameter values (e.g. making sure an int parameter is not "banana")? That's endpoint validation and belongs in the API layer.
  • Does this string value contain any encoded characters that the persistence store can't handle? This is the kind of validation that you'd put in the persistence layer.
  • Do we want to give the user immediate feedback about their input before they try to submit the form? That would require validation rules to be executed in the frontend. That kind of validation would inherently need to live in the frontend (unless you are okay with polling the BE for validation, which is not particularly efficient)

Like so many things, this isn't a predetermined answer that you can find online and then apply in your codebase. This is something that needs you to consider your own specific use case and find what works best for you.

By validation I mean checks like "value is integer and in range from 1 to 100" or "string matches this format"

These do sound to me like they're business rules, so the application layer is definitely a correct place for them.

Then there's the question of whether you want to also bake validation into the frontend. The benefit of doing so would be that you can provide the user immediate feedback instead of waiting for them to submit the form. The drawback is that you have to develop the same validation a second time (and usually you can't reuse logic between FE/BE as they tend to use different languages).

I strongly advise you don't take a FE-only validation approach, because FE-only validation can be circumvented by a malicious user who knows how to use their browser's dev tools or craft their own requests (e.g. using PostMan).
That being said, if malevolent use does not cause a problem for anyone except the user themselves, you can argue that you don't need to defend against malevolent use. But that is a dangerous call to make if you're wrong.

2
  • Thanks for your reply! By presentation layer as I said in the question I meant not frontend, but the layer of backend API where requests are received, decoded, passed to business logic layer and then formatting response
    – LNK
    Commented Feb 9 at 10:31
  • @LNK That's a reasonable ambiguity to point out but it's the same principle at play regardless
    – Flater
    Commented Feb 11 at 9:15
0

By validation I mean checks like "value is integer and in range from 1 to 100" or "string matches this format".

If what you are doing is parsing some general purpose representation to ensure that it conforms to some schema, you will normally want that to happen as close to the process boundary as you can conveniently manage.

If what you are doing is comparing the information to some current policy, or performing a comparison of that information to other information that has already been collected (ie: what's in the database), then that will normally be a domain model concern.

Example: some field in the input message is supposed to be RFC 4122 compliant UUID? That's a parsing concern that you put near the boundary. The UUID is supposed to match one of the known productId values in our system? That's a domain model concern.


If I could look back at one thing that improved my take on validation the most, it’s adherence to a closure of operations. I’d encourage you to go back to those sections on command/query separation and closure of operations in the Big Blue Book. Once I adhered to those two concepts, validation was merely another step in reporting the success or failure of an operation, and like many good simplifications, a host of symptoms I mistook for separate diseases just went away. -- Jimmy Bogard, 2009

0

Short answer: Both!

This is what we used to call Defensive Programming.
Checking that that the Data we've being given will "fit" into wherever we need to put that Data next and rejecting it if it doesn't.

At the Business level, we need to ensure that, say, a Transaction amount is numeric and has a "sensible" value, that a given UserId actually does exist in the backend system, that the Product whose Id we've been supplied actually exists and we've got enough of them to meet this order.

At the Presentation level, it's "nice" to check all of these things and give "immediate" feedback where we can and, indeed, most modern users would expect us to do just that. But that's for their benefit, as part of the User Experience, not ours, as part of our protecting our systems against duff Data.

In the world of the Web Application, nothing from the client should be trusted.
All the JavaScript (or whatever) validation code in the World can now be "edited away" by a sufficiently-determined user, armed with nothing more than their web browser.
Question: If you send an HTML SELECT list to the User to choose an option from, do you validate the value that comes back? You should ...

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