Architecture¶
Kwai tries to follow the clean architecture principles. The frontend is already a separated layer. The backend code is using the domain driven design (DDD) for modeling the software.
One of the rules of clean architecture is that when you don't own or control something, then keep it on the outside of your design or wrap it. This means for example, that the domain code isn't allowed to contain FastAPI code or database related code. The code for the API is on the outside because it's the entry point of a call to the system. Presenters are used to transform domain objects into JSON:API documents. The repository pattern keeps the database code on the outside. And interfaces are used to protect the inside from the outside.

The pendulum library is used for processing dates and timestamps. Because Kwai doesn't own this code, the pendulum code is wrapped into value objects (Timestamp, Date, ...). If the pendulum package is outdated, we only need to change these value objects.
This is also the reason why Pydantic isn't used for entities or value objects. Pydantic is great for validation and serialization, but we don't want Pydantic to become a dependency of the Kwai domain.
Dependency injection containers are only used on the outside. There should not be any magic code in the domain. So, dependency injection containers can only be used in the API entry code, the CLI entry code, ... From there on, the dependency should be passed as an argument (and passing it down should be done using an interface).
The Kwai structure can be divided in the following 3 types:
Note
This structure is also represented in the code. The backend uses a monorepo that contains a uv workspace with Python modules in the apps, bc and packages directories.
Applications¶
An application can depend on bounded contexts and packages.
There are currently 3 applications:
kwai_api¶
This is the REST API for Kwai. The API uses JSON:API as a standard. FastAPI is the Python web framework for handling the requests.
kwai_cli¶
This is the CLI for Kwai. The core of this application is Typer.
kwai_events¶
This application is used to process domain events. For the moment this is not used yet.
Bounded Contexts¶
The domain is separated into several bounded contexts. A bounded context is a domain area that contains related entities. A bounded context can depend on packages. To communicate with the outside world, dependency injection and interfaces must be used (i.e. Repositories, Presenters, ...).
Note
It is possible that the same entity is present in multiple bounded contexts. A member in the club bc is not the same as a member in the teams bc. A teams bc doesn't need to know where the member lives, while a club bc does need to now the contact address of each member.
Currently the following bc's are defined in Kwai:
club¶
Bounded context for everything that is related to members of a club.
identity¶
Bounded context for everything that is related to identity management: users, acces tokens, ...
portal¶
Bounded context for everything that is related for a portal: news, pages and other public information.
teams¶
Bounded context for everthing that is related to managing teams.
training¶
Bounded context for everything that is related to managing trainings.
Packages¶
kwai-core¶
This package contains code that can be reused in applications and bounded contexts. It defines value objects like Date, EmailAddress, ... It defines base classes for entities, presenters, ... It defines regularly used classes like Database, Query, ...
sql-smith¶
sql-smith is an SQL query builder. Originally developed seperatly, but since we are using uv workspaces the development has moved to the Kwai repository. sql-smith is used in the Repository and Query classes to build SQL statements.