How to Structure a Three-Tier Layered Architecture in Python

A mock three-tier architecture with presentation, business, and data layers that process a user request from input to response.

Medium Python 3.9+ Aug 9, 2026 System design patterns 12 views 0 copies

Python code

45 lines
Python 3.9+
class PresentationLayer:
    def __init__(self, business_layer):
        self.business = business_layer

    def handle_request(self, user_id):
        print(f"[Presentation] Received request for user {user_id}")
        data = self.business.process_user(user_id)
        print(f"[Presentation] Response: {data}")
        return data


class BusinessLayer:
    def __init__(self, data_layer):
        self.data = data_layer

    def process_user(self, user_id):
        print(f"[Business] Processing user {user_id}")
        if user_id <= 0:
            return {"error": "Invalid user ID"}
        user = self.data.get_user(user_id)
        if user is None:
            return {"error": "User not found"}
        return {"name": user["name"], "age": user["age"]}


class DataLayer:
    def __init__(self):
        self.users = {
            1: {"name": "Alice", "age": 30},
            2: {"name": "Bob", "age": 25},
            3: {"name": "Carol", "age": 35},
        }

    def get_user(self, user_id):
        print(f"[Data] Fetching user {user_id}")
        return self.users.get(user_id)


if __name__ == "__main__":
    data_layer = DataLayer()
    business_layer = BusinessLayer(data_layer)
    presentation_layer = PresentationLayer(business_layer)

    presentation_layer.handle_request(2)
    presentation_layer.handle_request(99)

Output

stdout
[Presentation] Received request for user 2
[Business] Processing user 2
[Data] Fetching user 2
[Presentation] Response: {'name': 'Bob', 'age': 25}
[Presentation] Received request for user 99
[Business] Processing user 99
[Data] Fetching user 99
[Presentation] Response: {'error': 'User not found'}

How it works

Each class represents a distinct layer with a clear responsibility: the presentation layer handles input and output, the business layer contains the application logic, and the data layer manages persistence. Dependencies flow downward — the presentation layer receives a business layer instance, and the business layer receives a data layer instance, following the dependency inversion principle. The code uses simple print statements to trace the flow, which makes it easy to see how a request propagates through the tiers. This separation allows you to replace or mock individual layers without affecting the others, improving testability and maintainability.

Common mistakes

  • Passing the data layer directly to the presentation layer, skipping the business logic
  • Hardcoding user data inside the business layer instead of keeping it in the data layer
  • Letting the presentation layer access data objects directly, breaking the layer boundaries
  • Not validating inputs at the business layer before accessing the data layer

Variations

  1. Use dependency injection frameworks or constructor injection to wire the layers
  2. Convert each layer to an abstract class or interface to enforce contracts

Real-world use cases

  • Web frameworks like Flask or Django scripts use an MVC-style split where views (presentation), models (data), and controllers (business) are separate modules.
  • Microservice backends separate API endpoints, domain services, and database repositories to allow independent testing and scaling.
  • Desktop GUI apps separate UI logic from business rules and database access for cleaner maintenance and unit testing.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from System design patterns

Related tutorials and quizzes for this topic.