Skip to content

Commit a5fff1d

Browse files
committed
docs: update documentation with project structure
1 parent 217a288 commit a5fff1d

File tree

9 files changed

+182
-3
lines changed

9 files changed

+182
-3
lines changed

README.md

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,21 @@ the [Cosmic Python](https://www.cosmicpython.com/) guidelines.
99

1010
* [Cosmic FastAPI](#cosmic-fastapi)
1111
* [Content](#content)
12-
* [Features](#features)
12+
* [About](#about)
13+
* [Features](#features)
14+
* [Project Structure](#project-structure)
15+
* [Environment Variables](#environment-variables)
16+
* [Recommended Directory Structure](#recommended-directory-structure)
17+
* [Domain Driven Design](#domain-driven-design)
18+
* [Models](#models)
19+
* [Entities](#entities)
20+
* [Value Objects](#value-objects)
21+
* [Aggregates:](#aggregates)
22+
* [Schemas](#schemas)
23+
* [Event Driven Architecture](#event-driven-architecture)
24+
* [Commands](#commands)
25+
* [Events](#events)
26+
* [Clean Architecture](#clean-architecture)
1327
* [Continuous Integration](#continuous-integration)
1428
* [Development Environment](#development-environment)
1529
* [Installing Poetry](#installing-poetry)
@@ -109,6 +123,123 @@ useful hints about what kinds of object we’ll find in each file. We can use th
109123
We may even consider splitting our models, schemas, events and commands into separate packages and files if they get too
110124
big.
111125

126+
### Domain Driven Design
127+
128+
`Commands`, `Events`, `Schemas` and `Models` are the building blocks of our **Domain**.
129+
130+
#### Models
131+
132+
First, we define our domain models. These are the objects that represent the business concepts we’re working with.
133+
They should be as simple as possible, and contain only the attributes that are essential to the business, utilizing the
134+
business jargon. The idea is that, if you were to show these models to a non-technical person, but someone who
135+
understand the business process, they would be able to understand what the application does. Models encapsulate the
136+
behavior, state, and business rules that govern the application. Models can encompass entities, aggregates, and
137+
sometimes even Value Objects.
138+
139+
##### Entities
140+
141+
Entities are objects that have distinct identities that run throughout their lifecycle. In other words, an entity is
142+
defined not just by its attributes, but also by a unique identifier that differentiates it from other entities of the
143+
same type. Entities are mutable and can have their attributes modified while maintaining the same identity. They are
144+
often used to represent real-world objects or concepts that have an ongoing existence.
145+
146+
For example, in an e-commerce system, a "Product" can be an entity. Each product has a unique identifier, and its
147+
attributes (such as name, description, price) can change without changing its identity.
148+
149+
##### Value Objects
150+
151+
A Value Object is a concept from Domain-Driven Design (DDD). It's an object that represents a descriptive aspect of the
152+
domain with no conceptual identity. In other words, a `Value Object` is defined solely by its attributes, and two Value
153+
Objects with the same attributes are considered equal. They are immutable and can be thought of as "flyweight" objects
154+
that are shared whenever their values are the same.
155+
156+
Value Objects can be part of a `Model`. In fact, they often enhance the expressiveness and maintainability of Models.
157+
Value Objects help to define attributes with semantic meaning and encapsulate their validation and behavior. In some
158+
cases, a Model might consist of one or more entities and Value Objects that work together to represent and manage the
159+
business logic and data.
160+
161+
##### Aggregates:
162+
163+
Aggregates are clusters of related `entities` and `value objects` that are treated as a single unit. The aggregate is
164+
the boundary within which changes are managed and consistency is maintained. One entity within the aggregate is
165+
designated as the "aggregate root."
166+
All interactions with the aggregate are done through this root entity. This helps ensure that the integrity and
167+
consistency of the data is maintained within the aggregate.
168+
169+
For example, in the case of an e-commerce system, a "Shopping Cart" could be an aggregate. The shopping cart would be
170+
composed of multiple line items (entities representing products in the cart) and possibly other related information. All
171+
changes to the items in the cart would be managed through the shopping cart aggregate root.
172+
173+
Entities can be part of an aggregate, and an aggregate often includes one or more entities and possibly value objects.
174+
Aggregates define the transactional boundaries and consistency rules within the domain. They encapsulate business rules
175+
and enforce invariants to ensure that the data remains in a valid and consistent state.
176+
177+
#### Schemas
178+
179+
Schemas are used to define the structure and validation rules for the input and output data of your API. Schemas help
180+
ensure that data is correctly formatted and adheres to specific criteria before being processed. They are often used to
181+
validate request payloads and to define the shape of the data returned from API endpoints.
182+
183+
In this project we use `Pydantic` to define our schemas. [Pydantic](https://pydantic-docs.helpmanual.io/) is a library
184+
that provides runtime checking and validation.
185+
186+
#### Event Driven Architecture
187+
188+
`Commands` and `Events` are the building blocks of our **Event Driven Architecture**. You can consider both as simple
189+
dataclasses, as they have no behaviour.
190+
191+
Both commands and events are often used in software architectures to promote separation of concerns, modularity, and
192+
extensibility. By encapsulating actions or occurrences into discrete objects, it becomes easier to reason about the
193+
system and make changes without impacting other parts of the codebase.
194+
195+
In an API, we can use `commands` to represent requests from clients to perform certain actions. These commands may need
196+
to be validated before they can be processed. Once validated, they can be executed by an appropriate handler or service.
197+
Following this idea, we can use `events` to represent our API responses. For this, we can associate both as kind
198+
of `schemas`.
199+
200+
##### Commands
201+
202+
Commands represent actions or requests to be performed by a system. They typically encapsulate a specific intent or
203+
operation that needs to be executed. In software development, commands are often used in conjunction with a command
204+
pattern or a similar architectural pattern to decouple the sender of the command from its execution.
205+
206+
For example, in a web application, a command might be used to represent a user's request to update their profile
207+
information. The command object would contain the necessary data to carry out the update operation, and it would be
208+
executed by an appropriate handler or service.
209+
210+
##### Events
211+
212+
Events represent notifications or signals that something has happened within a system. They convey information about a
213+
specific occurrence and are often used for communication between different components or modules of an application.
214+
Events are typically used in event-driven architectures or publish-subscribe patterns.
215+
216+
When an event occurs, it can be published to an event bus or a similar mechanism. Other components that have subscribed
217+
to that event can receive and react to it accordingly. This allows for loose coupling and enables different parts of the
218+
system to respond to events without direct dependencies on each other.
219+
220+
For example, in an e-commerce application, an event might be triggered when a new order is placed. Subscribed
221+
components, such as inventory management or shipping modules, can then react to this event by updating their respective
222+
states or initiating further actions.
223+
224+
### Clean Architecture
225+
226+
The main idea behind Clean Architecture is to create a separation of concerns in software systems, allowing for easier
227+
maintenance, testing, and scalability. The core principle of Clean Architecture is the dependency rule, which states
228+
that dependencies should always point inward towards the core of the application and not outward towards external
229+
frameworks or tools. This helps keep the core of the application independent and flexible.
230+
231+
Clean Architecture typically consists of several layers, each with a specific responsibility:
232+
233+
* Entities: These are the core business objects and rules.
234+
* Use Cases (Interacts): These encapsulate the business logic and orchestrate interactions between entities.
235+
* Interfaces (Gateways): These define the interfaces that allow the use cases to interact with external data sources or
236+
systems.
237+
* Frameworks and Drivers: These are the outermost layers that deal with the infrastructure, such as databases, web
238+
frameworks, UI, etc.
239+
240+
In our project, we can easily observe the different layers according to our directory structure. It's an architecture
241+
that "screams": by its naming conventions, we can easily understand what is the responsibility of each module.
242+
112243
## Continuous Integration
113244

114245
This project uses `make` as an adaptation layer.

pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,5 +89,4 @@ testpaths = "tests"
8989
python_files = "test_*.py"
9090
junit_family = "xunit1"
9191
log_cli = false
92-
log_level = "DEBUG"
93-
adopts = "-p no:logging"
92+
log_level = "DEBUG"

src/template/adapters/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"""Adapters
2+
3+
A nod to the ports and adapters terminology. This will fill up with any other abstractions around
4+
external I/O (e.g., a redis_client.py). Strictly speaking, you would call these secondary adapters or driven
5+
adapters, or sometimes inward-facing adapters.
6+
"""

src/template/domain/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""Domain
2+
3+
A domain represents a specific area of functionality or business logic within an application.
4+
It defines a set of related concepts, data structures, and operations that are specific to that area of functionality.
5+
"""
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"""Commands
2+
3+
A command represents an intent to change the state of the system, it is a message that requests some action to be taken.
4+
5+
Commands are passed to command handlers, which interpret them and execute
6+
the corresponding actions to produce new events that update the system state.
7+
8+
Commands should be immutable, and their properties should be as minimal as possible.
9+
"""
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""Models
2+
Generally used in conjunction with an Object-Relational Mapping (ORM) library like SQLAlchemy or Tortoise-ORM.
3+
Models represent the structure of your application's data as it is stored in a database.
4+
They are classes that define the attributes and relationships of the data entities you work with in your application.
5+
"""
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"""Entrypoints
2+
3+
These are the places we drive our application from.
4+
In the official ports and adapters terminology, these are adapters too, and are referred to as primary, driving,
5+
or outward-facing adapters.
6+
"""
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""Application Service Layer.
2+
3+
Its job is to handle requests from the outside world and to orchestrate an operation. What we
4+
mean is that the service layer drives the application by following a bunch of simple steps:
5+
6+
* Get some data from the database
7+
8+
* Update the domain model
9+
10+
* Persist any changes
11+
12+
This is the kind of boring work that has to happen for every operation in your system, and keeping it separate from
13+
business logic helps to keep things tidy.
14+
"""

src/template/utils/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""Utils
2+
3+
This package contains utility functions and classes that are shared across all layers.
4+
"""

0 commit comments

Comments
 (0)