ADR-007 - Technologies (Backend)
| Status | accepted |
|---|---|
| Date | 2025-04-11 |
| Decision Makers | Dardan Bujupaj (AX), Marc Gähwiler (AX), Philipp Egli (Griesser) |
Context
The Sunny Calculator Add-On Application requires robust a backend service that can:
- Process calculation requests reliably and efficiently
- Integrate with Sunny API and other third-party systems
- Scale to handle variable load patterns
- Be maintainable by both AX (development) and Griesser teams (primary support)
All applications and services will run in Griesser’s Azure cloud environment. Support is structured with Griesser providing primary support, while AX handles issues that need deep intervention or code changes as per ADR-005 - Support and Operations.
Our application architecture divides into frontend (Web UI) and backend components as documented in ADR-006 - Application Architecture.
Decision
Runtime
The backend will run on as a Docker container as an Azure Container App.
Main Technical Decisions
| Technology | Description |
|---|---|
| Docker | Used to containerize the backend application, ensuring consistency between development, testing, and production. |
| Azure Container Apps | Azure-managed service to deploy and run Docker containers, offering scalability and seamless integration with Azure. |
| Python 3.13 | Chosen for its latest features, performance improvements, and compatibility with modern libraries. |
| Uvicorn | Lightweight, high-performance ASGI server for running FastAPI applications. |
Frameworks / Packages
| Use Case | Framework / Package | Description |
|---|---|---|
| Web Framework | FastAPI | Used to build fast, robust APIs with built-in OpenAPI documentation support and asynchronous capabilities. |
| Validation | pydantic | Integrated with FastAPI for request/response validation and data parsing. |
| API Client Generation | openapi-python-client | Generates API clients based on OpenAPI specifications and internally uses httpx. |
| Logging | logging or structlog | Logs will be sent to Azure Blob Storage for monitoring and diagnostics. (see ADR-005 - Support and Operations: Logging) |
Developer Tooling
| Use Case | Framework / Package | Description |
|---|---|---|
| Command Runner | Just | just is a handy way to save and run project-specific commands. |
| Pre-Commit Hooks | pre-commit | A framework for managing and maintaining multi-language pre-commit hooks. |
| Package Management | uv | An extremely fast Python package and project manager, written in Rust. |
| Linter & Code Formater | Ruff | An extremely fast Python linter and code formatter, written in Rust. |
| Type Checking | Pyright | Pyright is a full-featured, standards-compliant static type checker for Python. It is designed for high performance and can be used with large Python source bases. |
| Testing | pytest | The pytest framework makes it easy to write small, readable tests, and can scale to support complex functional testing for applications and libraries. |
| Test Coverage | pytest-cov | This plugin produces coverage reports. Compared to just using coverage run this plugin does some extras: |
| Network Interaction Recorder | pytest-recording | A pytest plugin that allows you recording of network interactions via VCR.py. |
| Mocks | pytest-mock | Thin-wrapper around the mock package for easier use with pytest |
| AsyncIO Tests | pytest-asyncio | pytest-asyncio is a pytest plugin. It facilitates testing of code that uses the asyncio library. |
| Abort Hanging Tests | pytest-timeout | pytest plugin to abort hanging tests. |
| Distributed Testing | pytest-xdist | pytest xdist plugin for distributed testing, most importantly across multiple CPUs |
Consequences
Chosen Approach
-
Adoption of Python and FastAPI:
- By choosing Python 3.13 and FastAPI, the development team benefits from a well-documented, developer-friendly framework with built-in OpenAPI support, allowing for quick development cycles.
- The asynchronous capabilities of FastAPI and Uvicorn ensure robust performance for handling concurrent requests, which is essential for the processing-heavy calculation tasks required by the Sunny Calculator Add-On.
-
Containerized Application Deployment:
- Using Docker for containerization ensures consistency across development, testing, and production environments.
- Deploying on Azure Container Apps provides managed scalability, seamless integration with Azure services, and minimizes administrative overhead for setup, maintenance, and scaling.
-
Familiarity vs. Expertise:
- The AX team has significant experience with Python, making development, debugging, and future maintenance more efficient.
- A possible downside is that Griesser’s team, primarily responsible for support, may face a slight learning curve due to their primary expertise in C# and .NET.
Drawbacks
- While this approach leverages AX’s expertise, Griesser’s team might need additional training to autonomously support the application.
- Complex business logic or critical integrations might require more collaboration between the AX and Griesser teams.
Alternatives
C#/ASP.Net
- Griesser’s development team has strong expertise in the ASP.Net ecosystem.
- Migrating the backend to C# with ASP.Net Core would make the application more aligned with Griesser’s existing technology stack and simplify future troubleshooting and onboarding for their support team.
Drawbacks
- AX developers have limited experience with C#/.NET, leading to a potential learning curve.
- With tight project deadlines, this alternative would pose a significant risk of delays and lead to increased initial development effort.
- Missteps due to unfamiliarity with the technology could introduce defects or inefficiencies into the codebase.
Azure Functions
- Running the backend on Azure Functions would simplify certain operational aspects, including scaling and reducing infrastructure management.
- This approach aligns with the serverless architecture principles, reducing the burden of managing containerized systems.
Drawbacks
- Azure Functions have inherent limitations for long-running processes or features relying on persistent open connections (e.g., streaming, WebSockets).
- Administering distributed functions would add complexity in debugging and monitoring, as the logic might become fragmented across many small units.
- Hosting a more complex backend on Azure Functions could lead to challenges in implementing synchronous operations or handling intricate processing pipelines.