Skip to main content

Framework for the application service layer, approximating Uncle Bob Martin's Clean Architecture.

Project description

Kerno is trying to be:

  • a framework for building applications in Python
  • which approximates Robert C. Martin’s Clean Architecture
  • by empowering a service layer (here called Action layer)
  • and moving both persistence and UI (including web frameworks) to the edges of the system, while still providing ways to make development and automated testing easier.

The documentation is at

Defense of the architecture

This is the first such project in the entire Python community, which seems so addicted to the insufficient MVC or MVT offered by common web frameworks. Kerno does not replace these frameworks, but facilitates creation of the top layers of an architecture that contains, from center to border:

  • Configuration layer: Such that all other layers have access to settings. Why should configuration settings be available only to the web framework???
  • Model layer: in which you define entities (e. g. SQLAlchemy models), but no I/O (no session, no queries). The entities can be seen by all the layers below.
  • Action layer: in which you define business rules (decisions, the core of your app), meaning it can import and use the models above, and use other utilities that are dependency-injected.
  • Repository layer: Performs database access (e. g. queries) and is dependency-injected into the Action layer, making unit tests easier to write.
  • Controller layer: Thin glue layer, the only one that imports and uses a web framework or GUI framework.
  • Template layer: If using a web framework.

In order to better understand why this architecture is good for medium to large applications, I encourage you to watch a couple of Uncle Bob Martin’s talks on this subject – easily found on the Internet.

Anyway, good architecture is about decoupling. Now I am going to tell you why you should decouple 1) your web framework and 2) your data persistence.

Action (or Service) layer

Do not conflate the core of your app – the business rules – with usage of a web framework. You may want to switch web frameworks in the future, or build an entirely different UI. Therefore, business rules must not reside in controllers – move your application’s decisions to a higher layer! Conversely, the layer that contains business rules (the Action layer) may NOT import anything from the web framework.

Your controllers should be thin – just get the data and call an Action.

Also do not conflate business rules with data persistence. If you keep I/O concerns out of your Service layer, here called Action layer, then this layer becomes truly unit-testable, which in itself is reason enough to do this. Integrated test (meaning a test that hits a database, even SQLite in memory) suites easily take 5+ minutes to run for large apps, making TDD (test first) impossible. The solution is to separate decisions in pure functions (which do no I/O) so they can be unit-tested. This way the unit tests run instantaneously and you are able to do TDD.

MVC or MVT is missing a Service layer between the Model and the Controller. This layer, here called Action, should contain the business rules in a pure way, leaving UI related preoccupations to a thin controller, and storage concerns to Models and the…

Repository layer

MVT, as implemented by most Python web frameworks, puts a data persistence layer in the center of the architecture, but Uncle Bob’s Clean Architecture teaches us this is wrong.

In our solution, the model entities may remain in the center and circulate (as data holders) through all layers – this has always been so convenient –, but the engine that stores and retrieves entities – here called Repository (in SQLAlchemy it’s the session and its queries) – must be a dependency-injected layer.

Fowler and Robert C. Martin would have us put the SQLAlchemy models away too, so in the future you could more easily swap SQLAlchemy with something else, or even develop multiple storages simultaneously: SQLAlchemy, ZODB etc., but this piece of advice I am not following yet.

(Even so, you might be able to pull this off right now, if you remember that you don’t have to use the declarative flavor of the SQLAlchemy ORM – you can instead declare tables, and then classes, and then mappers that map tables to the classes. These classes need no specific base class, so you are free to use them as your entities.)

For now, most importantly, you are able to develop a FakeRepository for fast unit testing of the Action layer – or even a MemoryRepository that stores all entities in RAM.

SQLAlchemy strategy

SQLAlchemy creates testability challenges since it is very hard to mock out, so the following rules must be followed:

  • The session must be present in the Repository layer, which is dependency-injected into the Action layer. This allows you to write fast unit tests for the Action layer – by injecting a FakeRepository object which never touches a RDBMS.
  • The session must NOT be present in the model layer (which defines entities). For now, I think relationships (e.g. User.addresses) and object_session(self) can continue to exist in models normally, as long as you think you can implement equivalent relationships in the other repository strategies with the same API.
  • The session must NOT be imported in the Action layer (which contains business rules). Really, only your Repository object can use the session.

Using Kerno

If you wish to adopt the Clean Architecture, then Kerno can help you. Here is how.

Startup time and request time

Kerno computes some things at startup and keeps the result in a “global” object which is an instance of the Kerno class. This instance is initialized with the app’s settings and utilities (strategies) are registered on it. Then it is used on each request to obtain globals. Each request consists of a call to an Action.

Component registration

In order to swap components between environments, Kerno could have used the famous and awesome Zope Component Architecture, but we are using Reg instead. Reg is very powerful and you don’t need to create an interface for each component you want to register.

Composable actions

Kerno provides a base class for actions (the service layer). If you follow the pattern, then you can create actions composed of other actions, which might be useful to you.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Files for kerno, version 0.2.0
Filename, size File type Python version Upload date Hashes
Filename, size kerno-0.2.0-py3-none-any.whl (16.2 kB) File type Wheel Python version 3.5 Upload date Hashes View
Filename, size kerno-0.2.0.tar.gz (10.7 kB) File type Source Python version None Upload date Hashes View

Supported by

Pingdom Pingdom Monitoring Google Google Object Storage and Download Analytics Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page