Sponsor: Do you build complex software systems? See how NServiceBus makes it easier to design, build, and manage software systems that use message queues to achieve loose coupling. Get started for free.
What are business rules? Well, they often get confused with trivial validation. I think it’s essential to make the distinction because where you apply business rules and validation will differ and indicate the type of system you’re building. Let me explain and illustrate how you can make the distinction with a simple code example.
Check out my YouTube channel, where I post all kinds of content accompanying my posts, including this video showing everything in this post.
Let’s immediately start with a very simple code example. Here’s a single method that has two conditions. The first is that if the quantity argument is less or equals to zero, we’re throwing an exception. The second is if the quantity argument is greater than the amount we currently have on hand in our system, we’re going to throw.
Don’t get hung up on the fact this is throwing exceptions, there are better approaches. Check out my post, Code Review & Refactoring to a Better Design.
So of these two conditional statements, are they Business Logic? Validation?
Trival or superficial logic is often static. The business isn’t likely to come to you and ask you to change how some rule is applied. They are often common, understood rules. An example of this would be an email address. If you accept an email address as input, trivial validation would be that it is in the correct format. Important note: that does not mean the email address exists. Once trivial validation logic is defined, it’s not likely to change.
It also exists more so in being defined at an outer layer at the edge of your system. This is because you often can translate to values to pass into the core of your system. An example is if you have an HTTP API, where you’re converting HTTP query string or context bodies into types that are them called into the core of your system. More on this later.
Lastly, trivial validation is often deterministic. Nothing dynamic at runtime needs to toggle or change the behavior. The same input is going to result in the same output.
On the flip side, business rules are almost the exact opposite in terms of characteristics. They aren’t static. They evolve. The business will decide if a rule needs to be changed or even removed entirely. Business logic will expand and evolve. Very often, business logic is based on the state. It often lives within the core of your system, not at the very edge.
Looking back at the code example, the very first condition is trivial validation logic. The second is business logic.
An option is having ShipProduct accept a valid quantity, meaning you must pass in a valid value. Some people will refer to this as avoiding “primitive obsession,”.
Creating a type that we can use that is valid from the start. Here’s a Quantity type that must be valid when it’s created and has an implicit conversion to an integer.
We can then change our ShipProduct to accept this as the argument. We can also remove the trivial validation that we had in this method.
As you can expect, we had that trivial validation also in a bunch of other methods, not shown, such as ReceiveProduct, InventoryAdjustment, and more. Now this trivial validation is defined by a single type.
As mentioned previously, this Quantity type is going to be forced upstream on our calling code, likely at the edge of our application. In the example of an HTTP API, we likely now are converting our inbound HTTP value which is an integer to a Quantity type.
Business Rules evolve. We may change our mind and instead of having a hard limit that we can’t ship any product if we don’t have the quantity on hand, we’re going to allow for a buffer.
That QuantityBuffer might be defined for the entire warehouse, maybe that’s defined per specific product. These rules can evolve and they’re based on the current state of the system.
Removing trivial validation will allow you to focus on business rules. In real-world systems, these can get very complex. Ridding yourself of trivial validation where business rules are defined will make it more focused.
Push trivial validation up the call static to the edge. This could be using model binding rules with a web framework or a separate library where you can define trivial validation rules when converting primitives to a request object.
Why does this matter? Too often, people are creating “domain models” when all they really have is trivial validation. They add needless complexity with entities, aggregates, repositories, and more when all you needed were trivial validation and transaction scripts. Check out my post Domain Logic: Where does it go?
Developer-level members of my Patreon or YouTube channel get access to a private Discord server to chat with other developers about Software Architecture and Design and access to source code for any working demo application I post on my blog or YouTube. Check out my Patreon or YouTube Membership for more info.