How dose it deploy? via docker-compose
How dose it deploy? via CI/CD and kubernetes
.env file parameters, put it next to the services
AUTH_BASE_URL = http://nginx-service/auth/api/v1
PRODUCT_BASE_URL = http://nginx-service/product/api/v1
ORDER_BASE_URL = http://nginx-service/order/api/v1
AUTH_DB_STACK = postgresql # postgresql, mongo_db
PRODUCT_DB_STACK = postgresql # postgresql, mongo_db
ORDER_DB_STACK = mongo_db # postgresql, mongo_db
MONGO_HOST = mongodb-service
MONGO_PORT = 27017
MONGO_INITDB_ROOT_USERNAME = root
MONGO_INITDB_ROOT_PASSWORD = rootpassword
MONGO_INITDB_DATABASE = db
POSTGRES_HOST = postgres-service
POSTGRES_PORT = 5432
POSTGRES_USER = admin
POSTGRES_PASSWORD = adminpassword
POSTGRES_DB = db
JWT_SECRET = 5fd4a7c9-7b61-49bf-8aea-ae8c53727290
JWT_ALGORITHM = HS256
JWT_EXPIRATION_MINUTES = 2
JWT_REFRESH_EXPIRATION_MINUTES = 4
GF_SECURITY_ADMIN_PASSWORD=admin
To run with docker-compose:
docker compose up -d
In this layer, the application infrastructure is defined, such as:
-
Authentication utilities such as token creation, management, and validation
-
Database client and its models (tables)
-
Errors related to this layer and other layers
- include status code and message
-
Services for interacting with external APIs
- include interfaces and their implementation
-
Fastapi config such as
- middleware
- tasks that should be run on startup or shutdown, such as create and close database client
- implement some states based on settings loaded from .env in main app, to have access them throughout the entire project
-
Mixin classes
-
Application settings load from the
.envfile- load with pydantic_settings
infra/
│
├── auth/
│ └── <files or directories...>
│
├── db/
│ ├── redis/
│ │ └── <files or directories...>
│ │
│ ├── mongodb/
│ │ └── <files or directories...>
│ │
│ └── sqlite/
│ └── <files or directories...>
│
├── exceptions/
│ └── <files...>
│
├── external_api/
│ ├── interface/
│ │ └── <files...>
│ │
│ └── service/
│ └── <files...>
│
├── fastapi_config/
│ └── <files...>
│
├── mixins/
│ └── <files...>
│
└── settings/
└── <files...>
In this layer, data models are defined that are only used inside the application, meaning between layers, for transferring data.
domain/
├── mock_data/
│ └── <files...>
│
└── schemas/
└── <schema_group_name>/
└── <files...>
In this layer, data models are defined that are only used for receiving or sending data to the client.
models/
├── filter/
│ └── <files...>
│
└── schemas/
└── <schema_group_name>/
└── <files...>
In this layer, communication with the database is handled. Repository classes are defined here, whose methods provide interaction with the database. Each repository class inherits from an interface defined in this layer. Interfaces define the structure of database communication, so we can have multiple repository classes based on a single interface and use them for dependency injection.
repo/
├── interface/
│ └── <files...>
│
└── <implementation_name>/
└── <files...>
Naming implementations can be based on:
- Storage type — for example:
sql,nosql
repo/
├── interface/
│ └── <files...>
│
├── sql/
│ └── <files...>
│
└── nosql/
└── <files...>
- Storage name — for example:
postgresql, ormongodb.
repo/
├── interface/
│ └── <files...>
│
├── postgresql/
│ └── <files...>
│
└── mongodb/
└── <files...>
In this layer, endpoints are defined along with their dependencies, response statuses, and other endpoint-related configurations.
routes/
├── api_endpoints/
│ ├── <endpoint_group_name>/
│ │ └── <files...>
│ │
│ ├── <endpoint_group_name>/
│ │ └── <files...>
│ │
│ └── main_router.py
│
├── depends/
│ └── <files...>
│
└── http_response/
└── <files...>
In this layer, the application’s business logic is defined. This layer acts as an important bridge between endpoints in the Routes layer, the database in the Repo layer, and external APIs in the Infra layer.
usecase/
├── <usecase_group_name>/
│ └── <files...>
│
└── <usecase_group_name>/
└── <files...>
The layers are not limited to the mentioned items and can also include other related configurations.
Configuring Docker Compose
Services:
- FastAPI app-services: Our application services ->
auth-service,product-service,order-service,admin-serviceanduser-service. - MongoDB service: Our NoSQL database, running on internal and external port
27017:27017. - PostgreSQL service: Our SQL database, running on internal port
5432. - pgAdmin service: Added for easier access to the PostgreSQL database. It runs on
8080:80, meaning we connect externally through port8080to the internal port80.
Volumes:
Both databases have their own storage volumes defined: pgdata and mongo_data.
Environment variables:
All environment variables for the services are read from the .env file.
Restart policy:
All services are set with restart: always. This ensures that whenever a container stops, it will automatically restart.
Service dependencies:
- The database services don’t depend on any other services.
- The FastAPI app depends on both database services. This means the databases must be fully created and running before the FastAPI app is built and started.
- The pgAdmin service also depends on the PostgreSQL service.
FastAPI service details:
- The build process for the FastAPI service starts with the
Dockerfilelocated in the root directory of the app. This file copies the app’s files and directories into the container’s work directory (/app) and installs the required packages fromrequirements.txt. - The container ports for this service are defined in the
.envfile, and theportssection of the Docker Compose file references these environment variables. - Since we can change the default port of the FastAPI app via the
.envfile, the command to run the app is placed at the end of the service definition in the Docker Compose file. This way, the application always uses the internal port defined in.env.
All Kubernetes manifests for our services, with databases services, nginx, prometheus, grafana and others, are organized and stored within the k8s directory in a structured manner.
-
Environment setup:
- All environment variables defined in the project repository’s Variables section are stored in a
.envfile.
- All environment variables defined in the project repository’s Variables section are stored in a
-
Docker registry login:
- Authenticate with the Docker registry.
-
Kubectl setup:
- Configure and initialize
kubectlwith Kubeconfig, set in the secrets and access withsecrets.KUBE_CONFIG.
- Configure and initialize
-
ConfigMap creation, generate all necessary ConfigMaps:
app-config– Contains environment variables from the.envfile.nginx-config– Built from thenginx.conffile for Nginx configuration.prometheus-config– Built from theprometheus.ymlfile for Prometheus configuration.
-
Secrets creation:
- Create docker registry secrets for use in the
imagePullSecretssection of the kubernetes manifests for our project services.
- Create docker registry secrets for use in the
-
Database services deployment:
- Deploy database manifests first, to ensure database services are available.
-
Build and push project services images:
- Build docker images for all project services and push them to the registry.
-
Project services deployment:
- Deploy all project services using their kubernetes manifests.
-
Other services deployment:
- Finally, deploy the manifests for other services such as Nginx, Prometheus, and Grafana.