Skip to content

Setup application documentation using github pages #75

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions .coveragerc

This file was deleted.

70 changes: 70 additions & 0 deletions .github/workflows/github-pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Deploy static content to Pages

on:
push:
branches: ["main"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false

jobs:
build:

permissions:
contents: read

environment:
name: github-pages

runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v3

- name: Set up Python 3.12
uses: actions/setup-python@v4
with:
python-version: "3.12"

- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install poetry
poetry config virtualenvs.create false
poetry install --no-root --with dev

- name: Build static pages
run: make docs-build

- name: Setup Pages
uses: actions/configure-pages@v4

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: './site'

deploy:
needs: build

# Grant GITHUB_TOKEN the permissions required to make a Pages deployment
permissions:
pages: write # to deploy to Pages
id-token: write # to verify the deployment originates from an appropriate source

environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}

runs-on: ubuntu-latest

steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.PHONY: docs docs-build

containers:
# Use local UID to avoid files permission issues when mounting directories
# We could do this at runtime, by specifying the user, but it's easier doing it
Expand Down Expand Up @@ -73,3 +75,9 @@ generate-proto:

fix: format-fix lint-fix
check: typing test format lint

docs:
poetry run mkdocs serve

docs-build:
poetry run mkdocs build
108 changes: 108 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Bootstrap python service
[![CI Pipeline](https://github.com/febus982/bootstrap-python-fastapi/actions/workflows/ci-pipeline.yml/badge.svg)](https://github.com/febus982/bootstrap-python-fastapi/actions/workflows/ci-pipeline.yml)
[![Python 3.9](https://github.com/febus982/bootstrap-python-fastapi/actions/workflows/python-3.9.yml/badge.svg?event=push)](https://github.com/febus982/bootstrap-python-fastapi/actions/workflows/python-3.9.yml)
[![Python 3.10](https://github.com/febus982/bootstrap-python-fastapi/actions/workflows/python-3.10.yml/badge.svg?event=push)](https://github.com/febus982/bootstrap-python-fastapi/actions/workflows/python-3.10.yml)
[![Python 3.11](https://github.com/febus982/bootstrap-python-fastapi/actions/workflows/python-3.11.yml/badge.svg?event=push)](https://github.com/febus982/bootstrap-python-fastapi/actions/workflows/python-3.11.yml)

[![Maintainability](https://api.codeclimate.com/v1/badges/a2ab183e64778e21ae14/maintainability)](https://codeclimate.com/github/febus982/bootstrap-python-fastapi/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/a2ab183e64778e21ae14/test_coverage)](https://codeclimate.com/github/febus982/bootstrap-python-fastapi/test_coverage)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v1.json)](https://github.com/charliermarsh/ruff)

This is an example implementation of microservice applying
concepts from [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
and [SOLID principles](https://en.wikipedia.org/wiki/SOLID).

* The repository classes are isolated behind interfaces, enforcing the [Interface Segregation principle](https://en.wikipedia.org/wiki/Interface_segregation_principle)
and the [Inversion of Control](https://en.wikipedia.org/wiki/Inversion_of_control) design pattern
* The application frameworks are decoupled from the domain logic
* The storage layer is decoupled from the domain logic

In this way our components are loosely coupled and the application logic
(the domains package) is completely independent of the chosen framework
and the persistence layer.

## HTTP API Docs and versioning

API documentation is provided by [FastAPI](https://fastapi.tiangolo.com/features/)
on `/docs` and `/redoc` paths using OpenAPI format.

I believe that versioning an API at resource level provides a much more
flexible approach than versioning the whole API.

The example `books` domain provides 2 endpoints to demonstrate this approach

* `/api/books/v1` (POST)
* `/api/books/v2` (POST)

## Package layers

This application is structured following the principles of Clean Architecture.
Higher level layers can import directly lower level layers. An inversion of control
pattern has to be used for lower level layers to use higher level ones.

Packages are ordered from the highest level to the lowest one.

------

* `http_app` (http presentation layer)
* `grpc_app` (grpc presentation layer)
* `storage` (database connection manager, repository implementation)

------

* `domains` (services, repository interfaces)

------

## Class dependency schema

![](architecture.png)

## Data flow and layers responsibilities

![](flow.png)

## How to run

Using Docker:

* `make containers`: Build containers
* `docker compose run --rm dev make migrate`: Run database migrations
* `docker compose up dev`: Run HTTP application with hot reload
* `docker compose up grpc`: Run GRPC application
* `docker compose run --rm test`: Run test suite

Locally:

* `make migrate`: Run database migrations
* `make install-dependencies`: Install requirements
* `make dev-dependencies`: Install dev requirements
* `make update-dependencies`: Updates requirements
* `make migrate`: Run database migrations
* `make dev`: Run HTTP application with hot reload
* `make grpc`: Run GRPC application
* `make test`: Run test suite

## Other commands for development

* `make generate-proto`: Generates grpcio python stubs from `.proto` files in `grpc_app/proto` directory
* `make check`: Run tests, code style and lint checks
* `make fix`: Run tests, code style and lint checks with automatic fixes (where possible)

## Multistage dockerfile configuration

Python docker image tend to become large after installing the application requirements
(the slim base is ~150 MB uncompressed), therefore it's important to spend efforts
to minimise the image size, even if it produces a slightly more complex multistage
Dockerfile.

The following setup makes sure the production image will keep to a minimal size ("only" 390MB):
* 150MB base image
* 165MB python installed dependencies
* 73MB poetry + updated pip

Using the following pipeline the "test" image is instead ~850MB, more than 400MB that would
end up as a cost in traffic on each image pull.

![](docker-container.png)
46 changes: 46 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# yaml-language-server: $schema=https://squidfunk.github.io/mkdocs-material/schema.json

site_name: Bootstrap python fastapi
site_description: 'A template for a python web service written applying Clean Architecture concepts'
site_author: 'Federico Busetti'
docs_dir: docs/
repo_name: 'febus982/bootstrap-python-fastapi'
repo_url: 'https://github.com/febus982/bootstrap-python-fastapi'

theme:
name: material
features:
- navigation.expand
- navigation.top
- content.code.copy

palette:

# Palette toggle for light mode
- scheme: default
primary: teal
toggle:
icon: material/brightness-7
name: Switch to dark mode

# Palette toggle for dark mode
- scheme: slate
primary: teal
toggle:
icon: material/brightness-4
name: Switch to light mode

extra:
social:
- icon: fontawesome/brands/linkedin
link: https://www.linkedin.com/in/federico-b-a0b78232

nav:
- Home: index.md

markdown_extensions:
- pymdownx.details
- pymdownx.blocks.admonition
- pymdownx.blocks.details
- pymdownx.snippets
- pymdownx.superfences
Loading