Enterprise Design Patterns: Real-World Examples from Patterns of Enterprise Application Architecture
Enterprise design patterns are proven solutions to common problems encountered when building business applications. Martin Fowler's Patterns of Enterprise Application Architecture catalog organizes these patterns into key categories. In this article, we'll explore some fundamental patterns with practical Python examples. 1. Domain Logic Patterns Transaction Script Pattern The simplest way to organize business logic, where procedures handle transactions from presentation to database. class OrderService: def create_order(self, customer_id, items): # Transaction script for order creation try: # Start transaction customer = CustomerRepository().find_by_id(customer_id) if not customer: raise ValueError("Customer not found") order = Order(customer) for item_data in items: product = ProductRepository().find_by_id(item_data['product_id']) if not product: raise ValueError(f"Product {item_data['product_id']} not found") order.add_item(product, item_data['quantity']) OrderRepository().save(order) # Commit transaction return order except Exception as e: # Rollback transaction raise e 2. Data Source Architectural Patterns Repository Pattern Mediates between domain and data mapping layers, acting like an in-memory collection of domain objects. from abc import ABC, abstractmethod class Repository(ABC): @abstractmethod def find_by_id(self, id): pass @abstractmethod def save(self, entity): pass class CustomerRepository(Repository): def find_by_id(self, id): # Implementation with SQLAlchemy return db.session.query(Customer).filter_by(id=id).first() def save(self, customer): db.session.add(customer) db.session.commit() 3. Object-Relational Behavioral Patterns Unit of Work Pattern Maintains a list of objects affected by a business transaction and coordinates writing changes. class UnitOfWork: def __init__(self): self.new_objects = [] self.dirty_objects = [] self.removed_objects = [] def register_new(self, obj): self.new_objects.append(obj) def register_dirty(self, obj): if obj not in self.new_objects and obj not in self.dirty_objects: self.dirty_objects.append(obj) def register_removed(self, obj): if obj in self.new_objects: self.new_objects.remove(obj) return self.removed_objects.append(obj) def commit(self): for obj in self.new_objects: repository_for(obj.__class__).add(obj) for obj in self.dirty_objects: repository_for(obj.__class__).update(obj) for obj in self.removed_objects: repository_for(obj.__class__).delete(obj) # Clear tracking after commit self.new_objects.clear() self.dirty_objects.clear() self.removed_objects.clear() 4. Web Presentation Patterns Model-View-Controller (MVC) Pattern Separates presentation, input processing, and business logic. # Flask example demonstrating MVC # Model (in models.py) class Product: def __init__(self, id, name, price): self.id = id self.name = name self.price = price # Controller (in controllers.py) class ProductController: def __init__(self): self.product_repository = ProductRepository() def list_products(self): return self.product_repository.find_all() def get_product(self, product_id): return self.product_repository.find_by_id(product_id) # View (templates/products.html) """ {% for product in products %} {{ product.name }} - ${{ product.price }} {% endfor %} """ # Route (app.py) @app.route('/products') def list_products(): controller = ProductController() products = controller.list_products() return render_template('products.html', products=products) 5. Distribution Patterns Data Transfer Object (DTO) Pattern Carries data between processes to reduce method calls. class ProductDTO: def __init__(self, id, name, price, description): self.id = id self.name = name self.price = price self.description = description @classmethod def from_domain(cls, product): return cls( id=product.id, name=product.name, price=product.price, description=product.description ) def to_dict(self): return { 'id': self.id, 'name': self.name, 'price': self.price, 'description': self.description } # Usage in an API endpoint @app.route('/api/products/') def get_product_api(product_id): product = ProductRepository().find_by_id(product_id) if not product: return jso

Enterprise design patterns are proven solutions to common problems encountered when building business applications. Martin Fowler's Patterns of Enterprise Application Architecture catalog organizes these patterns into key categories. In this article, we'll explore some fundamental patterns with practical Python examples.
1. Domain Logic Patterns
Transaction Script Pattern
The simplest way to organize business logic, where procedures handle transactions from presentation to database.
class OrderService:
def create_order(self, customer_id, items):
# Transaction script for order creation
try:
# Start transaction
customer = CustomerRepository().find_by_id(customer_id)
if not customer:
raise ValueError("Customer not found")
order = Order(customer)
for item_data in items:
product = ProductRepository().find_by_id(item_data['product_id'])
if not product:
raise ValueError(f"Product {item_data['product_id']} not found")
order.add_item(product, item_data['quantity'])
OrderRepository().save(order)
# Commit transaction
return order
except Exception as e:
# Rollback transaction
raise e
2. Data Source Architectural Patterns
Repository Pattern
Mediates between domain and data mapping layers, acting like an in-memory collection of domain objects.
from abc import ABC, abstractmethod
class Repository(ABC):
@abstractmethod
def find_by_id(self, id):
pass
@abstractmethod
def save(self, entity):
pass
class CustomerRepository(Repository):
def find_by_id(self, id):
# Implementation with SQLAlchemy
return db.session.query(Customer).filter_by(id=id).first()
def save(self, customer):
db.session.add(customer)
db.session.commit()
3. Object-Relational Behavioral Patterns
Unit of Work Pattern
Maintains a list of objects affected by a business transaction and coordinates writing changes.
class UnitOfWork:
def __init__(self):
self.new_objects = []
self.dirty_objects = []
self.removed_objects = []
def register_new(self, obj):
self.new_objects.append(obj)
def register_dirty(self, obj):
if obj not in self.new_objects and obj not in self.dirty_objects:
self.dirty_objects.append(obj)
def register_removed(self, obj):
if obj in self.new_objects:
self.new_objects.remove(obj)
return
self.removed_objects.append(obj)
def commit(self):
for obj in self.new_objects:
repository_for(obj.__class__).add(obj)
for obj in self.dirty_objects:
repository_for(obj.__class__).update(obj)
for obj in self.removed_objects:
repository_for(obj.__class__).delete(obj)
# Clear tracking after commit
self.new_objects.clear()
self.dirty_objects.clear()
self.removed_objects.clear()
4. Web Presentation Patterns
Model-View-Controller (MVC) Pattern
Separates presentation, input processing, and business logic.
# Flask example demonstrating MVC
# Model (in models.py)
class Product:
def __init__(self, id, name, price):
self.id = id
self.name = name
self.price = price
# Controller (in controllers.py)
class ProductController:
def __init__(self):
self.product_repository = ProductRepository()
def list_products(self):
return self.product_repository.find_all()
def get_product(self, product_id):
return self.product_repository.find_by_id(product_id)
# View (templates/products.html)
"""
{% for product in products %}
{{ product.name }} - ${{ product.price }}
{% endfor %}
"""
# Route (app.py)
@app.route('/products')
def list_products():
controller = ProductController()
products = controller.list_products()
return render_template('products.html', products=products)
5. Distribution Patterns
Data Transfer Object (DTO) Pattern
Carries data between processes to reduce method calls.
class ProductDTO:
def __init__(self, id, name, price, description):
self.id = id
self.name = name
self.price = price
self.description = description
@classmethod
def from_domain(cls, product):
return cls(
id=product.id,
name=product.name,
price=product.price,
description=product.description
)
def to_dict(self):
return {
'id': self.id,
'name': self.name,
'price': self.price,
'description': self.description
}
# Usage in an API endpoint
@app.route('/api/products/')
def get_product_api(product_id):
product = ProductRepository().find_by_id(product_id)
if not product:
return jsonify({'error': 'Product not found'}), 404
return jsonify(ProductDTO.from_domain(product).to_dict())
6. Base Patterns
Gateway Pattern
Encapsulates access to an external system or resource.
class PaymentGateway:
def __init__(self, api_key):
self.api_key = api_key
self.base_url = "https://api.paymentservice.com/v1"
def charge(self, amount, currency, card_token):
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
payload = {
"amount": amount,
"currency": currency,
"source": card_token
}
response = requests.post(
f"{self.base_url}/charges",
headers=headers,
json=payload
)
if response.status_code != 201:
raise PaymentError(response.json()['message'])
return response.json()
Conclusion
Enterprise design patterns provide structured approaches to common architectural challenges. The examples above demonstrate how these patterns can be implemented in Python for various aspects of enterprise applications:
- Transaction Script for straightforward business logic
- Repository for data access abstraction
- Unit of Work for transaction management
- MVC for web presentation
- DTO for efficient data transfer
- Gateway for external system integration
These patterns help create maintainable, scalable applications by promoting separation of concerns and reducing coupling between components. When applied appropriately, they can significantly improve the quality of enterprise software systems.