{"id":19736670,"url":"https://github.com/anyesh/clean-template","last_synced_at":"2026-05-31T23:31:55.547Z","repository":{"id":90180912,"uuid":"603911593","full_name":"Anyesh/clean-template","owner":"Anyesh","description":null,"archived":false,"fork":false,"pushed_at":"2024-11-24T05:35:47.000Z","size":613,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-09T06:16:55.274Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Anyesh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-02-19T23:04:26.000Z","updated_at":"2024-11-24T05:35:52.000Z","dependencies_parsed_at":"2023-11-23T07:25:39.790Z","dependency_job_id":"aa7f2c5e-ce7d-449a-af75-984ea6e62c13","html_url":"https://github.com/Anyesh/clean-template","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Anyesh/clean-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Anyesh%2Fclean-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Anyesh%2Fclean-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Anyesh%2Fclean-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Anyesh%2Fclean-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Anyesh","download_url":"https://codeload.github.com/Anyesh/clean-template/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Anyesh%2Fclean-template/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33753923,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-12T01:08:05.356Z","updated_at":"2026-05-31T23:31:55.527Z","avatar_url":"https://github.com/Anyesh.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Cookiecutter flask clean architecture\n\nThis is a reusable Python Flask template. The project is based on Flask in combination with SQLAlchemy ORM.\n\nComplete list of features the template provides:\n\n- [Onion architecture](#onion-architecture)\n- [Maintenance window support](#maintenance-window-support)\n- [SQLAlchemy ORM](#sqlalchemy-orm)\n- [Alembic Database migrations](#alembic-database-migrations)\n- [Local postgres database docker support](#local-postgres-database-docker-support)\n- [Tests and test containers integration](#tests-and-test-containers-integration)\n- [Service prefix](#service-prefix)\n- [Dependency injection](#dependency-injection)\n- [Service-repository design pattern](#service-repository-design-pattern)\n\n## Getting started\n\nTo start a new project, run the following command:\n\n```bash\ncookiecutter https://github.com/Anyesh/clean-template\n```\n\nThis will prompt you for some information about your project. The information\nyou provide will be used to populate the files in the new project directory.\n\n### Running the application locally\n\nTo run the application locally, you need to have a Postgres database running.\nYou can use the `run_postgres.sh` script in the `scripts` directory to run a Postgres container.\n\n```bash\n./scripts/run_postgres.sh\n```\n\nYou can then run the application with flask:\n\n```bash\nflask --app src/app run\n```\n\nor with gunicorn:\n\n```bash\ngunicorn wsgi:app -b  0.0.0.0:7000 --workers=1 --preload\n```\n\n## Onion Architecture\n\nThe application follows the Onion Architecture pattern. An article is written\nabout our experience integrating an onion architecture with flask in combination with\nSQL Alchemy ORM that can be found [here](./docs/onion-architecture-article.md).\n\nThis architecture is a design pattern that organizes the codebase of a software application into multiple layers, where the innermost layer\nis the domain layer and the outermost layer is the application layer. Each layer depends only on the layers inside of it and not on the layers outside of it,\ncreating a separation of concerns, allowing for a more maintainable and scalable codebase.\n\nFor this template we suggest using a service-repository design pattern. This template also provides\na set of abc meta classes that you can use to create your repositories and services.\nFor example implementations you can have a look at [Service-repository design pattern](#service-repository-design-pattern).\n\n## Maintenance window support\n\nThis template provides you with a maintenance window mode. To learn more about\nmaintenance windows in your service you can read this article [here](https://devblogs.microsoft.com/cse/2023/02/08/maintenance_window_db_migrations/)\n\nDuring maintenance mode, clients will receive an http 503 status code.\n\n### Activating maintenance mode\n\nYou can activate maintenance mode in the following ways:\n\n```bash\ncurl -X PATCH http://localhost:7000/\u003cservice-prefix\u003e/v1/service-context -d '{\"maintenance\": true}' -H 'Content-Type: application/json'\n```\n\nor via the command line:\n\n```bash\nflask activate_maintenance_mode\n```\n\n### Deactivating maintenance mode\n\nYou can deactivate maintenance mode in the following ways:\n\n```bash\ncurl -X PATCH http://localhost:7000/\u003cservice-prefix\u003e/v1/service-context -d '{\"maintenance\": false}' -H 'Content-Type: application/json'\n```\n\nor via the command line:\n\n```bash\nflask deactivate_maintenance_mode\n```\n\n## SQLAlchemy ORM\n\nThe template uses SQLAlchemy ORM for its database connection and database models\nintegration. Its is currently setup with postgres, however you can\nchange it to any other database that is supported by SQLAlchemy. For other databases\nhave a look at the official Flask SQLAlchemy documentation\nthat can be found [here](https://flask-sqlalchemy.palletsprojects.com/en/3.0.x/)\n\nThis template provides you with a model base class that you can use to create your models.\n\n```python\nfrom src.infrastructure.models.model_extension import ModelExtension\n\nclass User(ModelExtension):\n    __tablename__ = 'users'\n    id = Column(Integer, primary_key=True)\n    attribute_a = Column(String(50), nullable=False)\n    attribute_b = Column(String(50), nullable=False)\n\n    def __repr__(self):\n        return self.repr(id=self.id, attribute_a=self.attribute_a, attribute_b=self.attribute_b)\n```\n\n## Alembic database migrations\n\n\u003e Note: The application uses a postgres database. Make sure you have a postgres\n\u003e database running before running the following commands. For local development,\n\u003e you can use the run_postgres.sh script to run a postgres container locally.\n\n1. Make sure you have the diesel cli installed. You can install it with the following command:\n   ```bash\n   sh ./scripts/run_postgres.sh\n   ```\n2. Create a database migration\n   ```bash\n   flask db migrate -m \u003cmigration-message\u003e\n   ```\n3. Apply the database migration:\n   ```bash\n   flask db upgrade\n   ```\n\n## Local postgres database docker support\n\nYou can run a local postgres docker database by using the following script:\n\n```bash\n sh ./scripts/run_postgres.sh\n```\n\nThis will run a postgres docker container on port 5432. Also it will create a\n.env file in the root directory of the project. This file contains the database\nconnection string. The service will read this connection string from the .env file\nand use it to connect to the database.\n\n## Tests and test containers integration\n\nAll tests are can be found under the `tests` folder. When using the template\nyou can place all you tests in this folder.\n\nThe service uses [python unittest](https://docs.python.org/3/library/unittest.html) in combination\nwith [flask testing]\n\nTo run the tests, you can use the following command:\n\n```bash\npython -m unittest discover -s tests\n```\n\nYou can use the test containers library to run your tests against a postgres database.\nYou do this by `setup_database()` in your test class. This will create a postgres container\nand run your tests against it. After the tests are done, the container will be destroyed.\n\nIf you want to run your tests against a different database, you can change the\nsetyp_database method in the test class to use a different database container.\n\n```python\nclass Test(AppTestBase):\n\n    def setUp(self) -\u003e None:\n        super(Test, self).setUp()\n        self.setup_database()\n```\n\n## Service prefix\n\nThe application can use a service prefix for the endpoints.\nThe service prefix is defined in the config.py file. Given a service prefix\nof 'example', endpoints will be prefixed with '/example/v1/service-context'.\n\n```python\n# config.py\nSERVICE_PREFIX = os.environ.get(SERVICE_PREFIX, '')\n\n# middleware\nclass PrefixMiddleware(object):\n    ROUTE_NOT_FOUND_MESSAGE = \"This url does not belong to the app.\"\n\n    def __init__(self, app, prefix=''):\n        self.app = app\n        self.wsgi_app = app.wsgi_app\n        self.prefix = prefix\n\n    def __call__(self, environ, start_response):\n\n        if environ['PATH_INFO'].startswith(self.prefix):\n            environ['PATH_INFO'] = environ['PATH_INFO'][len(self.prefix):]\n            environ['SCRIPT_NAME'] = self.prefix\n            return self.wsgi_app(environ, start_response)\n        else:\n            start_response('404', [('Content-Type', 'text/plain')])\n            return [self.ROUTE_NOT_FOUND_MESSAGE.encode()]\n```\n\nDuring testing the service prefix is not applied. This allows you\nto test the endpoints without having to add the service prefix to the\nendpoint.\n\nIf the service prefix is not set, the service will not use a service prefix.\n\n## Dependency injection\n\nThis template uses the [dependency_injector](https://pypi.org/project/dependency-injector/) library\nfor dependency injection. The template provides you with a container class that you can use to\nregister your dependencies. The container class is located in the `src/dependency_container.py` file.\n\nYou can add your dependencies to the container and use them\nin your routers, services and repositories.\n\n## Service repository design pattern\n\nThis template provides you with a repository-service pattern. There are two base classes\nthat you can use to create your repositories and services. These base classes are located\nin the `src/services/repository_service.py` and `src/infrastructure/repositories/repository.py`.\n\n### Repository example\n\nA repository example can be seen below, this repository is used to query the MyModel model.\nFor custom query params support override the `_apply_query_params` method.\n\n```python\nfrom infrastructure.repositories import Repository\nfrom infrastructure.models import MyModel\n\nclass MyExampleRepository(Repository):\n    base_class = MyModel\n    DEFAULT_NOT_FOUND_MESSAGE = \"MyModel was not found\"\n\n    def _apply_query_params(self, query, query_params):\n        name_query_param = self.get_query_param(\"name\", query_params)\n\n        if name_query_param:\n           query = query.filter_by(name=name_query_param)\n\n        return query\n```\n\n### Service example\n\nA Service example can be seen below, this service expect a repository to be injected in its contuctor.\n\n```python\nfrom services.repository_service import RepositoryService\n\nclass MyExampleService(RepositoryService):\n    # The RepositoryService gives you access to crud repository operations by the inheritance\n    # RepositoryService.\n    pass\n\n# Can be instantiate by injecting the repository\nmy_example_service = MyExampleService(my_example_repository)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanyesh%2Fclean-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanyesh%2Fclean-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanyesh%2Fclean-template/lists"}