A day in the life of an Optimizely Developer - Vertical Slicing in CMS12
There is such a vast choice these days in how you can build a website, aside from all of the different programming languages out there, there are many different methodologies and patterns that can be used.
In this blog post, I cover off Vertical Slicing, which is a method that we adopt widely in all of our CMS 12 builds. I personally believe this is a way of ensuring we stick to the important concept of SRP (Single Responsibility Principle) as well as being one of the better ways of structuring projects within an Agile delivery program.
The traditional way of building .Net applications is via a concept called the Horizontal Layering Approach. This is a software design pattern that divides an application into distinct layers, each with a specific responsibility, to promote separation of concerns, improve maintainability, and facilitate testing and scalability.
The following are the common layers of a typical .Net Core application:
Presentation Layer: This is the topmost layer of the application that deals with user interface components such as forms, pages, and controls. It is responsible for receiving user inputs and displaying the results to the user. It communicates with the business logic layer to perform actions and retrieve data.
Business Logic Layer: This layer contains the business logic of the application, which is responsible for processing the data and applying business rules. It contains the application's core functionality and domain-specific logic. The business logic layer communicates with the data access layer to retrieve or store data.
Data Access Layer: This layer provides an interface to access the data store, such as a database or file system. It is responsible for querying and retrieving data from the data store and providing it to the business logic layer. The data access layer also handles data validation, caching, and transaction management.
By separating the application into these distinct layers, developers can better manage complexity, maintain code quality, and achieve greater flexibility and scalability. Changes made in one layer do not affect the other layers, allowing for easier maintenance and modification. Additionally, each layer can be tested independently, which helps to identify issues early in the development process.
Optimizely CMS12 Example
As CMS 12 is a pre-built application, we do not have the data access layer as described above, however as you can see from the screenshot below of the out-of-the-box AlloyCMS example, the way in which the code is structured does still have distinct horizontal layering.
- Business - this folder contains business logic used by the different blocks, pages and other general components
- Controllers - this folder contains the individual controllers for the page types of the website
- Models - this folder contains all of the models used by Content Types (Pages/Blocks), ViewModels, etc.
- Views - this folder contains all of the views for pages, blocks, etc.
The functionality for a specific feature is clearly spread across multiple folders within the solution and thus increases complexity, testing overhead and can lead to perfomance issues if not implemented correctly.
Vertical slicing is a software development approach where a feature is implemented in a single vertical slice of the application's architecture, spanning the user interface down to the data access layer. In ASP.NET Core applications, this approach is often used to build small, independent, and self-contained parts of a larger application.
The idea behind vertical slicing is to create a clear separation of concerns within the application, where each slice represents a specific feature or business capability. Each slice includes all the layers required to implement the feature, such as the user interface, business logic, data access, and data storage. By doing this, the developer can work on a specific feature end-to-end, without being blocked by other parts of the application.
Some of the benefits of vertical slicing in ASP.NET Core applications include:
- Easier to maintain, all the code is in one place and does not have to be hunted down.
- Reduced test surface when changing a single feature
- Due to decoupling between features while coupling tightly within a feature
- Has a cost of duplication of some code while maintaining that decoupled natu
CMS 12 Example
In the example CMS 12 solution structure below (taken from one of our client builds), you can see that we have a "Features" project, this contains a "Common" folder which contains common functionality that is either utilised by a number of different components or is functionality global to Optimizely, examples being Editor Descriptors, Api Controllers, Extensions, Helpers, etc.
The other folder in this solution (although not required, this is just for additional nesting) is the "Components" folder, this has the following sub-folders:
- Blocks - this contains sub-folders for each block content type in the application, each folder encapsulating all of the functionality that the block requires
- Pages - this contains sub-folders for each page content type in the application, each folder encapsulating all of the functionality that the page requires
- Global - this contains sub-folders for each global feature within the application, each folder encapsulating all of the functionality for that feature, examples being footer, header, navigation, breadcrumb, etc.
In the example below, we have the AccordionBlock folder, this contains:
- AccordionBlock.cs - this is the model class for the accordion block content type
- AccordionBlockValidator.cs - this is the IValidate class for validation of the accordion block fields
- AccordionContainerBlock.cs - this is the model class for the accordion container block content type (this block contains one or more accordion block content types)
- AccordionContainerBlockViewComponent - this is the ViewComponent class which encapsulate all of the functionality for the accordion block feature
- AccordionContainerViewModel - this is the view model that is passed into the View file
- AccordionViewModel - this is the view model that is passed into the View file as part of AccordionContainerViewModel
The next screenshot shows the "Web" project, this contains the standard "Views" folder, with sub-folders for the different pages in the application, there is also a "Components" folder (although not required, this is just for additional nesting) which contains folders for all of the components (blocks and global) that exist in the features project. Within each of these folders is a single Index.cshtml file which is the View file for the related View Component.
It is clear to see that this method of structuring your applications, provides a much cleaner and more concise way of grouping all of the functionality and related files for a feature into easily distinguishable folders.
In summary, while the layered approach to building a .Net Core application does have many benefits, it also has some drawbacks that developers should be aware of. Here are some potential cons to consider:
Increased complexity: The layered approach can make the application more complex as it requires more code and more layers of abstraction. This can lead to increased development time and greater difficulty in maintaining the application.
Performance issues: The layered approach can lead to performance issues if not implemented correctly. Each layer adds an overhead that can slow down the application. Additionally, excessive or inefficient data access calls can cause delays in data retrieval. Also performance issues can arise due to repositories and services doing more than they need to do for the intended usage.
Over-engineering: The layered approach can lead to over-engineering if the application does not require a high level of complexity. This can lead to unnecessary code and can make the application difficult to understand and maintain.
Testing overhead: While testing each layer separately can help identify issues early on, it can also lead to increased testing overhead. Testing each layer separately can be time-consuming and may require more resources.
Lack of flexibility: The layered approach can make the application less flexible as it can be difficult to make changes that affect multiple layers. This can lead to code duplication and increased maintenance costs.
- Lots of model mappers needed for moving between layers
The main benefit of a layered approach is that:
- You are able to swap out a layer, namely swap out presentation or database while keeping the rest, although usages of this are extremely few and far between
When we look at Vertical Slicing however we can see that it has the following pros:
Faster feedback: With vertical slicing, developers can quickly get feedback on the feature they are building as they work through all layers of the application. This helps to identify issues early and address them before they become bigger problems.
Better user experience: Vertical slicing ensures that features are built from end to end, which helps to ensure a seamless user experience. This can lead to higher user satisfaction and increased adoption of the application.
Greater agility: Vertical slicing enables developers to quickly deliver features and respond to changing requirements. As each feature is built from end to end, developers can quickly make changes to any layer of the application without affecting other features.
Improved collaboration: Vertical slicing promotes collaboration among developers as they work together to build features from end to end. This approach also helps to ensure that developers have a better understanding of how all layers of the application work together.
Better maintainability: Vertical slicing can lead to better maintainability as developers can quickly understand how a feature is built and make changes as needed. This can help to reduce the amount of technical debt in the application.
So in conclusion, while the layered approach has its benefits, vertical slicing can be a useful alternative approach for .NET developers, particularly in projects where speed, user experience, and agility are critical.
Lead .Net Developer
Great post! Did you know that there is also a pizza architecture? :)
@valdis I assume you're therefore referring to articles such as this then: https://blog.tech-fellow.net/2016/10/17/baking-round-shaped-software/
Though I do think the first image on this reddit thread is funny and takes the Italian food element further: https://www.reddit.com/r/ProgrammerHumor/comments/b0bx62/pizzaoriented_architecture/
ravioli is the best :)
Reminds me of this joke from a while back...