Structuring data driven applications in Laravel

Structuring data driven applications in Laravel


What is a "Data driven application"?

We all know that Laravel makes it really easy to store and query data from your database. Getting data submitted from a form and storing it using an eloquent model is trivial. But what if your application needs to do a lot of non-trivial operations based on the data that you have? What if you have pieces of functionality that need to be reusable during different processes? The usual way of structuring this type of functionality by having functions spread around inside models and services can quickly fall apart and make the larger process harder to decipher.

The problem

Imagine you want to keep track of your little brother's grades during the school year. The school emails you a report every week of his grades but you're tired of having to find the email and check it every time.

Also imagine that you want to have some pretty charts where you can see how his grades have fluctuated over the grading period and even over the entire year.

When planning out the project you could make a flowchart that looks something like this:

datadrivenexample

Very simplistic flowchart but it'll work for now.

The question is: How can you transfer a flowchart like this into code in a way that clearly communicates what is being done? This brings us to the concepts we're going to introduce in this post. Actions and Processes

Actions

Think of an action as a small piece of logic to be taken on a piece of data that will always produce the same result. What is highlighted in yellow on the flowchart can be classified as an action:

ExtractSubjectsFromReportAction

ExtractGradesFromSubjectsAction

StoreReportAction

An action should be named clearly and specifically to what it does. And it should always produce an expected result. Hard conditional logic should not happen in an action. This would be part of a process.

Processes

What is highlighted in blue on the flowchart is a process. Let's get into an example of how we can combine actions and processes together to create an easily readable and maintainable piece of code that mimics the flowchart we currently have.

Let's start with the process of getting the latest report from the inbox and checking if we already have it in the database.

getlatestreport

The implementation of the actions is not that important for this example. At the end of the day they should do what you expect so you only have to worry about writing the process without thinking about the minutia of the implementation.

Let's take a look at what our parse report process could look like:

parsereport

Advantages

A lot of small implementation details were excluded in the example above, but hopefully it's enough to show that processes like this can be written in a way that is easy to follow without having a lot of methods all over the place that might only be used once.

Let's discuss some advantages:

Reusability

Not only can actions be re-used, but processes can as well. Imagine if you wanted to check for the latest report on a scheduled command. It would be as easy as:

command

What if you wanted in in a job?

What if you wanted to trigger it from an api endpoint like a webhook?

Since the core logic is contained inside the process, you can now trigger it from wherever you want without having to worry about implementation details or dependencies you might miss.

Readability

If you come back to your project after 2 weeks (enough time for you to forget everything as we all know), but you know that your main logic is inside a process, it will be much easier to understand what is happening. Your code will reflect your flowchart a lot closer than if you had, let's say, put all the functionality accross your models, controllers and services. Now you only need to look at your actions and processes and you're on your way.

Final note

At the end of the day, the concept of actions and processes are just naming conventions for a larger idea: translating a mental model into code

Hopefully it can also help you think about problems in a new way. Sometimes we find ourselves trying to warp our thinking into the programming patterns we're used to, but that might not always work. This might give you a different way to approach complex data problems in the future.