A solution to the Python Tech Task - A modern, domain-driven design (DDD) implementation of a name origin analysis API. This service provides insights into the probable origins of names based on statistical analysis and country data.
This project implements a service that predicts a person's likely country of origin based on their name and enriches it with additional country information. It integrates with two external APIs:
- Nationalize.io - for nationality prediction
- REST Countries - for country data enrichment
- Name Origin Analysis: Determine the probable country of origin for given names
- Country Information: Rich country data including flags, capitals, and geographical information
- RESTful API: FastAPI-based REST API with OpenAPI documentation
- Domain-Driven Design: Clean architecture with clear separation of concerns
- Testing: Comprehensive test suite with pytest
- CI: GitHub Actions workflow for continuous integration (testing & Formating).
- Code Quality: Pre-commit hooks and linting tools
To start using the application, you only need:
-
Docker and Docker Compose installed
-
Clone the repository:
git clone https://github.com/yourusername/name-origin-api.git cd name-origin-api -
Copy the environment files:
cp .env.example .env cp .env.prod.example .env.prod
-
Start the application using either:
With Make:
make all make migrations
Or directly with Docker Compose:
# Start the application docker compose --profile dev -f docker_compose/storages.yaml -f docker_compose/app.yaml --env-file .env up --build -d # Run migrations docker exec -t main-app alembic upgrade head
-
Start exploring the API at:
- Swagger UI:
http://localhost:8000/api/docs - ReDoc:
http://localhost:8000/api/redoc
- Swagger UI:
That's it! You're ready to use the application.
Note
You can run
make init-countries
after applying migrations that will fetch all countries from api, in order to achieve better optimization
The project follows Domain-Driven Design principles with a clean architecture:
.github/ # Github actions
app/
βββ alembic/ # Alembic, for sql migrations
βββ application/ # Application layer (API endpoints, schemas)
βββ domain/ # Domain layer (entities, value objects)
βββ infra/ # Infrastructure layer (repositories, models)
βββ logic/ # Business logic layer (commands, handlers)
βββ scripts/ # Python scripts
βββ scripts/ # Settings, include the configuration of the system
βββ tests/ # Test suite
- Python 3.13+: Modern Python features and type hints
- FastAPI: High-performance web framework
- SQLAlchemy: SQL toolkit and ORM
- Pydantic: Data validation and settings management
- Alembic: Database migrations
- Pytest: Testing framework
- Ruff: Fast Python linter
- Pre-commit: Git hooks for code quality
- Docker: Containerization with separate configurations for development and production
- Development container includes testing and development libraries
- Production container contains only necessary production dependencies
- GitHub Actions: CI pipeline
- uv 0.6.7: Fast Python package installer and resolver
- Python 3.13 or higher
- Docker and Docker Compose (for containerized setup)
- Git
- GNU Make
The project uses Make commands to simplify common development and deployment tasks. Here's a comprehensive list of available commands:
make all- Starts all services in development modemake app- Starts only the application in development modemake storages- Starts only the storage services (database, etc.)make test- Runs the test suitemake migrations- Runs database migrationsmake app-logs- Shows application logs in real-timemake app-shell- Opens a shell inside the application containermake all-down- Stops all development services
make all-prod- Starts all services in production modemake app-prod- Starts only the application in production modemake storages-prod- Starts storage services in production modemake migrations-prod- Runs database migrations in productionmake all-prod-down- Stops all production services
make downgrade- Rolls back the last migrationmake downgrade-prod- Rolls back the last migration in productionmake create-migration- Creates a new migration filemake create-migration-prod- Creates a new migration file in productionmake migrations-and-init- Runs migrations and initializes the containermake init-countries- Initializes country data in developmentmake init-countries-prod- Initializes country data in production
The Make commands use Docker Compose profiles (dev and prod) to manage different environments and configurations. Each command is designed to work with the appropriate environment variables and Docker Compose files.
The project supports two Docker container configurations:
- Development: Includes all development and testing tools, suitable for local development
- Production: Contains only necessary production dependencies, optimized for deployment
Once the server is running, access the API documentation at:
- Swagger UI:
http://localhost:8000/api/docs - ReDoc:
http://localhost:8000/api/redoc
-
GET /names/?name={name}
- Returns information about the most likely countries associated with a given name
- Integrates with Nationalize.io and REST Countries APIs
-
GET /popular-names/?country={country_code}
- Returns the top 5 most frequent names associated with a country
- Uses country code (e.g., "US", "UA")
The project uses GitHub Actions for continuous integration:
- Validate: Runs pre-commit hooks and code formatting
- Test: Executes the test suite
The project includes comprehensive tests:
- Unit tests for domain logic
- Integration tests for repositories
- API endpoint tests
- End-to-end tests
Run tests with:
make testThe project follows strict code style guidelines:
- Ruff for linting and formatting
- isort for import sorting
- mypy for type checking
Pre-commit hooks ensure code quality before commits.
- Implemented clean architecture with clear separation of concerns
- Used value objects for domain concepts
- Applied repository pattern for data access
- Implemented unit of work pattern for transaction management
- Pre-fetching all countries in the first request since the operation is time-consuming, and with only around 250 countries, we can store them all efficiently
- Implemented input validation using Pydantic and dataclasses schemas to ensure data integrity
- Secured environment variable handling to protect sensitive configuration
- Additional development time required for proper domain modeling
- More boilerplate code compared to simpler architectures
- Capital could be implemented as a foreign key in the future, but it's not necessary for the current requirements
- Border countries could be normalized into a separate table, which would increase complexity but only result in a maximum of 2500 rows (compared to 250), which is manageable for this system
- Add cache (Redis, Memcached, ...)
This project is licensed under the MIT License - see the LICENSE file for details.
- Bashar Hasan - Abstract-333