From State Management to Business Logic
To introduce a good architecture you need to separate your State from Business Logic
In this post, I want to talk about the idea of separating State Code from Business logic and my process of implementing it.
Understanding the idea and having a process to do it gives you another concrete step to take when it comes to decoupling your codebase. Taking these steps gives you outsized returns when it comes to maintaining and scaling your codebase.
Similar to how Flutter is taught to beginners in a way that corners them into writing unmaintainable code, once they understand there are state management solutions, they implement them (separate State from UI) but never take the next step, splitting business logic from state management. This creates a new problem that can cause issues regarding the robustness and reliability of your business logic.
Until your Business Logic / Rules is not separate from your State management, you cannot claim to be building a decoupled system.
In this post you will learn:
The definition I use for business logic
The definition I use for State Code
How to Split Business Logic from State Code
The benefits of splitting these apart
Leaving this in projects where the code won’t be growing, the team won’t grow and the product won’t be maintained for 2+ years is not a problem. But if you’re looking to build a product that will be built on and scaled over years, then understanding what’s in this post will go a long way.
After I first experienced proper decoupling, I never built a code base again that doesn’t have the basic concerns separated.
How I Define Business Logic
There are long discussions about what business logic is, the difference between business logic and business rules, and how engineers group them. I fall into the camp where the two kind of mix together, just because I don’t want to introduce another concept.
I personally have had a hard time when it comes to remembering theory, names, and labels for certain types of code and design patterns, but I’ve never had a problem building anything I can think of. I value any definition that allows you to properly execute and implement what you have in your mind.
With that said, I define business logic as the code that binds together what the user wants to do and the actions that eventually happen (Making an HTTP request, writing to a database, sending a notification, etc). Whereas state code either stores the state, exposes the state, or alters the state, which directly reflects in the User Interface on the screen.
As an example: Let’s say in a Food Delivery Service or product, the user adds an item to their cart. This is what the process would look like:
User taps “Add to Cart”: UI Code
The UI calls into the ViewModel: UI Code
The ViewModel sets the button to busy: State Code
We then make sure the product belongs to the same vendor as the others in the cart: Business Rule (Logic)
Then check if the item is already in cart and either updates count or adds a new one: Business Logic
Then we save the cart to the local database: Application Logic
When all is complete we set the button back to not busy: State Code
This isn’t all the actions that happen, but it’s to help illustrate the point of my distinction. 4 and 5 can be classified as business logic, and 4 would/could be classified as a Business Rule. But because they make no difference to me I prefer to keep it as Business Logic, for the simplicity of teaching, learning, and remembering.
How to split Business Logic from State
Now that you can identify the difference between the two it’s easier to group them together and then split the two apart. When I talk about splitting code apart I usually mean the code has to live in a separate file, not only in a separate function.
But even knowing that it’s easier to first split code apart into a function, locally, which can then more easily be moved into a separate file. My process is as follows:
Identify the different pieces of code: Here you’ll treat your Application Code as Business Logic as well, we want anything that’s not State Code to be split apart in this step.
Split into single responsibility functions: You’ll see that often there’s a natural grouping due to the synchronous manner things happen. Thinking back to the example above. After setting the button to busy, every step after can be grouped into a separate function. In this case we’ll move 4,5, and 6 into a separate function.
Move the single responsibility functions into their own class: Now we have the function, that clearly has nothing to do with the state of the application to the user. We can now create a class called
CartServicewhich will bear the responsibility to manipulate the cart and ensure it follows the correct rules.
Construct the class locally and use the single function call in place of 4, 5 and 6: As a first step, we construct locally and create a hard dependency. In the next post we’ll break those apart even more.
This split now gives us a 3 layered application that can scale independently. The UI can grow without making the state code unmaintainable. The state can grow without creating code overload for the UI, and the Business Logic can be assessed separately from both of that, on its own which will make it easier to spot bugs and issues in the logic.
Having this now also means you can test your business logic separately from your state logic which means more focused tests, and testing smaller units of work. It takes time, but as you see it’s not a very complex system, you use the basics of programming to give your code space to grow, making it easier for you to maintain and easier to expand on in the future.