Enterprise applications power the world's businesses, from banks to e-commerce platforms. Building robust, maintainable, and scalable enterprise systems requires not just good code, but the right architectural patterns. In this article, we'll explore several patterns from Martin Fowler's Catalog of Patterns of Enterprise Application Architecture, see how they solve real-world problems, and implement one in Python.
What Are Enterprise Design Patterns?
Enterprise design patterns are proven solutions to common problems encountered when building large, complex business systems. They provide best practices for organizing code, managing data, handling business logic, and supporting scalability.
Martin Fowler's catalog is a goldmine for anyone serious about enterprise development. Key patterns include:
- Layered Architecture
- Domain Model
- Data Mapper
- Service Layer
- Repository
- Active Record
- Table Module
- Unit of Work
- (and many more)
Real-World Example: Service Layer and Repository Patterns in Python
Letβs take a common scenario: building a user management system for an enterprise app.
We'll use the Service Layer pattern to encapsulate business logic and the Repository pattern to abstract data access. This separation makes our code maintainable and testable.
1. Layered Architecture
We split our app into layers:
- Presentation Layer: Handles HTTP requests, CLI, or UI.
- Service Layer: Business logic.
- Data Access Layer: Interacts with the database.
2. Repository Pattern
The Repository acts as a mediator between the domain and data mapping layers, providing access to data objects without exposing implementation details.
3. Service Layer Pattern
Encapsulates business logic, orchestrating domain objects and repositories.
Python Implementation Example
# domain.py
class User:
def __init__(self, user_id, name, email):
self.user_id = user_id
self.name = name
self.email = email
# repository.py
class UserRepository:
def __init__(self):
self._users = {}
def add(self, user):
self._users[user.user_id] = user
def get(self, user_id):
return self._users.get(user_id)
def list_all(self):
return list(self._users.values())
# service.py
class UserService:
def __init__(self, repository: UserRepository):
self.repository = repository
def register_user(self, user_id, name, email):
if self.repository.get(user_id):
raise Exception("User already exists!")
user = User(user_id, name, email)
self.repository.add(user)
return user
def get_user(self, user_id):
return self.repository.get(user_id)
def list_users(self):
return self.repository.list_all()
# main.py (presentation layer)
if __name__ == "__main__":
repo = UserRepository()
service = UserService(repo)
service.register_user(1, "Alice", "alice@example.com")
service.register_user(2, "Bob", "bob@example.com")
print("All users:")
for user in service.list_users():
print(f"{user.user_id}: {user.name} ({user.email})")
Benefits:
- Separation of concerns: Business logic is separated from data access.
- Testability: Service and repository layers can be tested independently.
- Maintainability: Changes in storage (for example, moving from in-memory to a database) do not affect business logic.
Other Patterns to Explore
- Unit of Work: Manages changes to business objects and coordinates writing out changes as a single transaction.
- Active Record: Domain objects also handle their own persistence.
- Table Module: Encapsulates all business logic for all rows in a database table.
- Domain Model: Complex business logic is captured in domain objects.
Repository Link
You can find a full implementation and more examples in the companion repository:
https://github.com/SebastianFuentesAvalos/article02_enterprise-design-patterns
References
Written by Sebastian Fuentes Avalos
Top comments (1)
good article ...bravo!!!