I’ve written before about keeping things simple in software design. One such way to do this that I preach nearly daily is to give things a singular purpose. The moment you introduce multiple purposes is the moment you introduce complexity and most likely the need for a refactoring session in the very near future.
When I started professionally in software, our budget wasn’t large. Mostly, I had to figure things out on my own. I can recall a function we introduced called Search. At first, this function took about four parameters and returned a single output type, but after several software revisions the function took about twenty parameters and could return several different output types depending on an output parameter that told the developer which form was being returned. Why strongly type output? That’s too easy. As you can imagine, this was where the idea of singular purpose started to make sense to me. Making my programs call a SearchThis function or a SearchThat function rather than all programs calling a single Search function.
How do we achieve singular purpose in design? This question can start a great session of paralysis by analysis if you let it. Most programming I do nowadays is object oriented, but this principle applies to any programming style or framework. Singular purpose starts at the application level, then goes to the class level, then to the method level, and even in the variables you declare and how you name them. It exists throughout the design.
SOA (Service Oriented Architecture) was our first attempt, organizationally, to bring singular purpose to the forefront of our designs from the ground up. This was a new way to think about programming, and building applications that provide single services and building a pipeline of navigation through the single service apps to solve a problem. This paradigm changed the way we build applications. In fact, most of our application design today follows the SOA pattern.
We started by chaining Windows services together with MSMQ (Microsoft Message Queue) and pushing a state object through different services that performed actions and appended data to our state object. We have experimented with other queues such as RabbitMQ and we have added caching servers into play such as Redis. We have even migrated these services to the cloud and used both AWS and Microsoft Azure native services to help drive these systems.
SOA is not the silver bullet when it comes to singular purpose. An example from our experience was that we wanted to build a pipeline that was only dependent on the database at the beginning and end of the journey. At some point in the build, one of the developers realized we needed to append information to our state object that came from the database from other processes that kicked off after the state object started on the pipeline. Rather than change when the pipeline started, we eventually caved, and added a service to append the necessary fields of information. This was still keeping with singular purpose, so no harm, no foul. However, because of code reuse, and maybe some impatience by some of the development staff, the database code that was added to a single service was also added to a second service, and then a third. At this point, I was no longer running this group, but from afar it became clear to me that singular purpose and SOA aren’t necessarily equal. The team was still following SOA, but they introduced complexity by having services that did one function and appended information from a database, as well as logged information to a database.
The lesson learned from the above story is that singular purpose has to be as important in your architecture as the problem you are solving. As I mentioned before, if not, you will be in for refactoring sessions sooner rather than later. The team that introduced the database into the several steps in our SOA design, also had to refactor the database out of the equation. They were contaminating their data from all directions and had to only pick specific points in which to allow new data into the model. This was the original design, and the correct solution to the problem at hand. Because we did not account for all scenarios up front, it became the easy way out to add a function to a single service rather than add a single service to the pipeline. It took a while for me to understand why, but I think I have figured it out. We need a system for SOA that makes it just as easy to add a service to the pipeline as it is to introduce a function to a service.
Recently, I have discovered Microsoft Azure WebJobs. I am so new to working with them, I can’t say they are the silver bullet to both SOA and singular purpose, but in my small experience with them, things are looking promising. Expect an entire blog post dedicated to them very soon, but look them up. They provide a framework to do very singular purposed design by exposing just a single function and allowing it to be called on a schedule or have it run continuously and even be triggered by events like messages in queues or files being uploaded. Very cool stuff! I am currently building my first SOA style system with them and I will provide a complete review very soon. The best part about them, is that they run in the space you have dedicated to an App Service in Azure, which is where you run your websites. These jobs can run either across the farm for your site, or as a singleton.
Singular purpose in software architecture is a key component to simple software design. It has to be as important as the problem you are solving to build long lasting, scalable, maintainable software systems. SOA is a good pattern to help reinforce singular purpose, but it is still up to you, as the developer, to make sure singular purpose is at the forefront of your thoughts and your actions. There are many tools, services, and frameworks available to help you in your quest for singular purpose. I am encouraged by WebJobs and I will provide an update as to my thoughts on them in the near future. Ultimately, it is on your shoulders to build specific apps, classes, and methods that tell a single story, rather than every story that needs to be told. When we launched the Search method with twenty parameters, we found many years of headaches maintaining a poorly designed function that found itself interwoven into much of our infrastructure instead of the many hours or days of pain it would have been to refactor it out, or even better yet, the hours of contemplation about keeping things simple that could have saved us that complexity in the first place. Learn from this and happy coding! 🙂