During my recent studies on microservice architecture I've stumbled upon the notion of "vertical slice architecture". I first ran across it in some reference to Gary Woodfinde's post "How to Implement Vertical Slice Architecture" and then later back to its source in "Vertical Slice Architecture" by Jimmy Bogard . This led me to vertical slice architecture examples that I've found in books ( Architecting ASP.NET Core Applications - Third Edition ) , training videos (Gill Cleeren's ASP.NET Core Clean Architecture On Pluralsight) , source code on GitHub (Jimmy Bogard's vertical slice version of the Microsoft .NET Core Contoso University sample application) and libraries supposedly geared towards vertical slice architecture such as MediaR . I found this quite interesting as a few years back while working on an older, layer based monolith, I myself began to create and add new libraries based on features rather than just add code/classes to existing layers without ever having heard of vertical slice architecture. The question is why did I begin to do this as usually it's a "no no" to go against established standards or patterns for an existing application. Well I'm going to tell you why and it's got as much or more to do about revenue and success for the business I worked for at the time than it does about technology.
The application that I was working on at the time was a long lived ASP.NET WebForms application that the business relied on heavily as an internal facing configuration and management tool. The application supported many diverse use cases and was structured in typical n-tier fashion with each layer implemented as its own project/assembly. There was a layer for data access, another for business entities with lots of business logic implemented in the data access as stored procedures or in the "code behind". This wasn't the organization's only application with similar layout and design which created a lot of challenges for the business. There were two large applications like this; one customer facing and another this internal facing config/management tool. Both applications were under constant development as new features were added to the business's platform to keep existing customers and to lure/sell to new clients as well. One of the primary challenges of this architecture was it prevented automated unit test which made the testing new features as well as regression testing time consuming and included a lot of manual testing. A the time I arrived at this organization the dev team had begun to break out business logic into separate projects/assemblies and writing unit tests for them so things were beginning to head in a better direction albeit with too many useless layers. I introduced the idea of using Model View Presenter which further helped to reduce "code behind" and move that code into libraries making the code easier to understand and increase the amount of code that could be covered by automated tests. Despite these efforts there were still some key problems. While the team had begun to use interfaces and "pure" dependency injection , the implementation of interfaces lived in the same assemblies as the definition of the interfaces (probably violating both the Common Closure and Stable Abstractions Principles) resulting in a high level of coupling amongst the layers, despite the reduced coupling between classes themselves.
The business never allocated the time or money to invest in upgrading the technology stack from WebForms to MVC as they were always struggling with time and development resources just to keep existing clients happy and draw new ones in to achieve the revenue growth they desired. Despite this I knew that we could make large increases in our development velocity if we could incrementally improve the architecture. My plan was to help the business by further implementing not only Martin's SOLID principles, but also his much lesser known package principles as well. While the widely known SOLID principles guide the design of interfaces and classes, Martin's lessor known package principles guide the design, the architecture, of libraries, assemblies, executables. By doing so I knew that we could reduce coupling, reducing the cost of testing and regression testing, improve quality and reduce our development cycle time thus increasing velocity and therefore shorten time to increased revenue.
There's nothing inherently wrong with layers within an application as they can help achieve the open-closed principle. It's when layers become abstractions that are hard to understand and often do little more than delegate to another layer, they become a bottle neck to development progress. These applications had this exact problem in many places so much so that I coined the architecture "7 layer bean dip". A great appetizer, but makes for poor software architecture. The other observation that I made quickly was in typical layer only fashion one had to add new entities or add fields to existing entities and similarly update the data access layer when adding support for new or updated use cases to the application. Since everything in the application, top to bottom, depended upon the entities assembly and the data access assemblies, this meant a full build of the application for even the tiniest of modifications and of course created the risk of some sort of regression issues in an unrelated part of the application. More bottlenecks to development and the business.
Thus I began to write new, separate projects/assemblies as containers for new presentation logic, new entities, new business logic and new data access. As these new projects/assemblies didn't have the same poor dependency structure as the rest of the application, this approach kept build times and therefore the "code and test" cycle much smaller and faster, the possibility of broad automated test coverage high and the risk of regression much closer to zero - e.g. this was a way to achieve the Open-Closed Principle (and Common Closure and Stable Abstractions). With this approach I was bringing vertical slices into the application architecture, while keeping the notion of layers as well. I believe that this combination of traditional layers and vertical slices by feature (use cases, modules, whatever you'd like to label it) is actually the best way to architect not only traditional monolith applications - which even Sam Newman says should be your default/starting architecture - but any decently sized micro service as well. I feel that using vertical slices only can be a bit of an extreme, knee jerk reaction to the a layering approach gone bad, e.g. throwing the baby out with the bath water.
So what's the real world litmas test for good application architecture? How does one know if you've got good architecture? Well in my book, rather than follow trendy notions or acronyms of "micro this" or "SPA that", one must ask what makes for a clear, easily maintainable and extendable code structure and success for the business? You go back to the basics, you've got to measure it. What do we measure? Well from a software standpoint we can start with cohesion and coupling. But how can you quantitatively measure these? Well Martin developed a basic set of metrics for OO design based on exactly these. These are well published and well known so I'll not cover them here. I teach my clients and students these so if you need help, reach out. The other thing to measure of course would be speed to market and feature/function utilization by customers.
The business I worked for with these applications was a financial software as a service business. Their early offerings started out with the customer/client facing application, but as the world evolved our larger clients began to demand consumption of our services via API integration. Thus the business shifted and began to offer API integration in addition to the more traditional Web UI offering. All of these applications in one way or another were about invoice generation and payment processing including support for credit card payments. The business needed a credit card payment processing API to support not only new API endpoints, but the existing automated processes with behind Web UI's and it needed to do so while achieving the highest level of PCI certification and of course scale. I was charged with developing a credit card API that could route different customers/clients to different payment gateways depending on their business requirements. Using this same simultaneous layering and vertical slicing approach I developed for the UI applications described earlier, I did exactly this. Over time the business needed to continuously add support for new gateway integrations to obtain new features clients wanted such as international payments, support for Canadian banking, larger volumes, faster payouts, etc. After developing a framework to support this payment processing API, typical development time to add a new gateway was on the order of days and deployment would consist of literally copying a single new DLL/assembly to the bin folder of the Web API and making a small configuration so that the API would recognize the new integration. It actually took the business and the payment gateway provider more time to implement, finalize and certify the new integration than it took to code it. For once the software development organization could go faster than the business could. A rare and great problem to have right? That is the ultimate litmus test with regards to layers, slices and the Open-Closed principle, etc. On the business front this credit card processing API processed on the order of billions of dollars annually and collected up to 75% of the business's revenue. I'd say that is within the window of success from both a technical and a business/financial standpoint.
The moral of the story here is that implementing a software architecture based on trends and popularity isn't necessarily a recipe for success. It's worth taking a look at vertical slice architecture and maybe some of its supporting libraries, but it's important to do so with an eye towards the underlying principles of high cohesion, low coupling, SOLID principles and perhaps even more importantly the application of Martin's package principles which have been around for more than twenty five years. Just as design patterns can be helpful if applied in the right circumstances and can create a nightmares when incorrectly applied, you must take the time to understand and correctly apply the underlying principles of any given architecture for the best outcome rather just picking a popular framework or a library.