This project demonstrates a refactoring pattern where a piece of functionality (saving data) within a monolithic application is extracted into a separate microservice. It uses Python, Flask, and asynchronous programming (asyncio, aiofiles, httpx).
The goal is to show how to decouple functionality using interfaces and dependency injection, allowing the main application to switch between a local implementation (saving to a file directly) and a remote implementation (calling a microservice API) without changing its core logic.
monolith_to_microservice_python/
├── .venv/ # Virtual environment (recommended)
├── entities/ # Data structures (e.g., Beer dataclass)
│ ├── __init__.py
│ └── beer.py
├── interfaces/ # Abstract base classes defining contracts
│ ├── __init__.py
│ └── action_service.py # Defines IActionService
├── implementations/ # Concrete implementations of interfaces
│ ├── __init__.py
│ ├── file_saver.py # Saves data to a local file
│ └── http_file_saver.py # Calls the microservice API
├── monolith_app/ # The original "monolithic" part
│ ├── __init__.py
│ ├── execute.py # Class using the action service
│ ├── main.py # Main script to run scenarios
│ └── logs/ # Logs generated by the local file saver
│ └── monolith_local_log.txt
├── microservice_api/ # The extracted microservice (Flask API)
│ ├── __init__.py
│ ├── app.py # Flask application
│ ├── requirements_api.txt # Dependencies for the API
│ └── logs/ # Logs generated by the API
│ └── api_log.txt
├── requirements.txt # Dependencies for monolith & HTTP client
└── README.md # This file
- Clone the repository (if applicable)
- Create and activate a virtual environment:
python -m venv .venv # Windows .\.venv\Scripts\activate # macOS/Linux source .venv/bin/activate
- Install dependencies for the Monolith/Client:
pip install -r requirements.txt
- Install dependencies for the Microservice API:
pip install -r microservice_api/requirements_api.txt
You need two terminals (or run the API in the background).
-
Terminal 1: Run the Microservice API Navigate to the project root directory and run the Flask app using an ASGI server like Hypercorn:
# Make sure your virtual environment is active # Ensure Flask[async] is installed (check requirements_api.txt) python -m hypercorn microservice_api.app:app --bind localhost:7183
(Note: The port
7183is used in the example code, matching the C# version's convention. The code inmicroservice_api/app.pyexplicitly binds to this). You should see output indicating the server is running. -
Terminal 2: Run the Monolith Scenarios Navigate to the project root directory and run the main script:
# Make sure your virtual environment is active python -m monolith_app.mainThis script will execute two scenarios:
- Saving data using the local
FileSaver. Checkmonolith_app/logs/monolith_local_log.txt. - Saving data by calling the running microservice API via
HttpFileSaver. Checkmicroservice_api/logs/api_log.txt.
- Saving data using the local
- Dependency Injection: The
Executeclass receives its dependency (IActionService) via its constructor, allowing different implementations (FileSaver,HttpFileSaver) to be injected. - Interfaces (Abstract Base Classes):
IActionServicedefines a contract that concrete implementations must follow. - Decoupling: The
monolith_appdoesn't need to know the details of how the action is performed, only that it conforms to theIActionServiceinterface. - Asynchronous Operations: Uses
async/awaitfor file I/O (aiofiles) and HTTP requests (httpx) to avoid blocking. - Microservice API: A simple Flask API provides the saving functionality over HTTP.