Using layered architectural patterns for better testability

Friday, November 20, 2020

Introduction

Still today, we see often testability as the necessary evil. Designing software architecture in a way that you can test your code upfront is seen as self-evident but in reality this is rarely the case. Finding the right architectural pattern really depends on your business requirements. And testability and code quality is not always the main priority, especially when you have a tight schedule. But  how can we provide a more testable design without sacrificing time-to-market?

We usually tend to implement our applications in a layers or divide it into different components. But what exactly is a layered architecture? Are there different types of layered architectural patterns? And how can we structure our application to support more testability? These questions I want to answer in this first part of my new series "Design For Testability".

What is a layered architecture?

Layered architecture in the object oriented programming paradigm defines a system as a complex entity that can be decomposed into interacting layers. They are highly decoupled components which communicate among each other through interfaces.

 

Hierarchical layers of independent components

 

Traditional layered architecture patterns always try to separate UI, business logic and the database. These areas can be seen as layers which themselves consists of several services or components. All layers are tightly coupled with the infrastructure of an application. Usually these layers have a vertical hierarchy where high-level components depend on the low-level components functionality.

But this is not absolutely necessary! The components are actually completely independent of each other and layered architectural patterns do not necessarily consist of a UI. A higher level layer can be any kind of client that consumes information from a lower level layer.

There are great examples of architectural patterns which try get rid of the hierarchical structure and make use of the Dependency Inversion principle, like the Onion Architecture or the Hexagonal Architecture. Besides the two patterns, mentioned above, there are also other well known types of layered architectural patterns are of course MVC, MVVM, PAC or Microkernel.

Why is it useful for testability?

So why should a layered architectural pattern suite more testability? Well, it is easier to isolate the system under test when creating unit tests. Each layer provides explicit and stable interfaces for other layers. The implementation details are internal and not visible for outside components. Therefore we avoid tight coupling and interdependencies. Therefore it is easier to test components independently.

Of course, you can do that with many different architectural patterns as well. But if we focus on non-hierarchical approaches, like the Onion Architecture, the main advantage of layered architectures is that our tests can be seen as a separate layer as well. This testing layer can contain all kind of automated code testing, like:

  • unit tests
  • integration tests
  • end-to-end tests

Our tests have full access to our application and domain logic due to the dependency inversion. We have a high degree of freedom for creating stable and clean tests.

How to structure your application?

It really depends on your use case and your chosen architectural pattern. But for object oriented programming languages, it is always useful to keep your tests in a separate test project.

Keeping your tests all at one place makes it easier to find them by Testing Frameworks. Most of modern testing frameworks are pretty good in discovering your tests and all modern IDEs provide some kind of Test Explorer to give the developer a better overview of all your tests.

Let's say, we have to build a business application called, FooBusiness App. So eventually, you could structure your application like this:

       

Depending on your use case and your architectural pattern, you can have much more folders, of course. Each folder is one layer. Each layer can contain more folders (feature folders) which consist of classes and interfaces. Of course, you have to be careful with your naming so other developers can understand your intent.

 

Conclusion

Even though Layered architectural patterns have some drawbacks in terms of scalability and development time, it is always useful to structure your application that it suites your business requirements. So here are my key takeaways:

  • Go with layered architecture to provide loose coupling and high separation of concerns.
  • For being more independent of infrastructure and testing, use the onion architecture
  • Testing should be a separate layer. This layer consists of all kind of tests (unit, integration, end-to-end).
  • Keep your tests in a separate dedicated test project.

 

Let me know what you think and stay tuned for part 2 of this series. If you can't wait, you can also check out my video on "Design for Testability".

Let us know what you
think about this article