DEV Community

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
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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 %}
  <div>{{ product.name }} - ${{ product.price }}</div>
{% endfor %}
"""

# Route (app.py)
@app.route('/products')
def list_products():
    controller = ProductController()
    products = controller.list_products()
    return render_template('products.html', products=products)
Enter fullscreen mode Exit fullscreen mode

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/<int:product_id>')
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())
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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:

  1. Transaction Script for straightforward business logic
  2. Repository for data access abstraction
  3. Unit of Work for transaction management
  4. MVC for web presentation
  5. DTO for efficient data transfer
  6. 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.

Top comments (1)

Collapse
 
jhon_thomasticonachambi profile image
Jhon Thomas TICONA CHAMBI

Great summary of enterprise design patterns. I found it very helpful how you grouped the patterns by category and explained them with practical Python examples. This makes them much easier to understand, especially for those coming from other languages or just starting to apply these architectures. It would be interesting to see a more complete example in the future that integrates several of these patterns into a single business flow. Well done!