You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Clean Architecture理解困惑:用户数据持久化场景技术问询

Breaking Down User Input Persistence with Clean Architecture

Hey there! Let's walk through this user input persistence scenario step by step—since you already grasp the core Clean Architecture rule (inner layers know nothing about outer ones, only interact via boundary interfaces), we'll map this directly to that principle to clear up your confusion.

First, let's recap the Clean Architecture layers from innermost to outermost: Entities → Use Cases → Interface Adapters → Frameworks & Drivers. Each layer only depends on the layer directly inside it, or on interfaces defined in inner layers.

Mapping Your Scenario to the Layers

Let's break down exactly how a user input (like a web form or console entry) flows to persistence while following the rules:

1. Innermost: Entities Layer

This is your core business domain—no external dependencies allowed. For your scenario, this would be something like a User entity, which holds only essential business properties and rules (e.g., email validation). It has zero knowledge of UI, databases, or any outer layer.

2. Next: Use Cases Layer

This is where you define your business use cases (like "create a user and save them") and the boundary interfaces that outer layers will implement.

The key here: you define an interface (let's call it UserRepository) in this inner layer with a method like save(User). The Use Case depends on this interface, but has no clue how it's implemented (that's for outer layers to handle). This keeps the inner layer decoupled from storage details.

3. Middle: Interface Adapters Layer

This layer acts as the "translator" between outer layers and inner layers:

  • It takes raw input from the UI (like form data or console strings) and converts it into a format the Use Case understands (e.g., mapping form fields to a User entity or use case input DTO).
  • It implements the UserRepository interface defined in the Use Cases layer—here's where you write the actual persistence logic (e.g., SQL queries, MongoDB operations).

4. Outermost: Frameworks & Drivers Layer

This is your UI (web form, console) and any external tools (database drivers, web frameworks). The UI triggers an event (like a form submission), which calls a controller in the Interface Adapters layer to handle the flow.

Example Code Walkthrough (Python)

Let's make this concrete with simple, readable code:

Entities Layer

class User:
    def __init__(self, user_id: str, name: str, email: str):
        # Core business rule: validate email format
        if "@" not in email:
            raise ValueError("Invalid email address")
        self.user_id = user_id
        self.name = name
        self.email = email

Use Cases Layer

from abc import ABC, abstractmethod
from entities.user import User

# Boundary interface DEFINED IN THE INNER LAYER
class UserRepository(ABC):
    @abstractmethod
    def save(self, user: User) -> None:
        pass

# The actual use case, depends only on the interface
class CreateUserUseCase:
    def __init__(self, user_repository: UserRepository):
        # Dependency injection: we get the interface implementation from outside
        self.user_repository = user_repository

    def execute(self, user_input: dict) -> User:
        # Business logic: generate ID, create valid User entity
        new_user = User(
            user_id=f"user_{hash(user_input['email'])}",
            name=user_input["name"],
            email=user_input["email"]
        )
        # Call the boundary interface—we don't care how save works
        self.user_repository.save(new_user)
        return new_user

Interface Adapters Layer

from use_cases.user_repository import UserRepository
from entities.user import User
import sqlite3

# Implement the inner-layer interface with actual storage logic
class SqlUserRepository(UserRepository):
    def save(self, user: User) -> None:
        conn = sqlite3.connect("app_db.db")
        cursor = conn.cursor()
        cursor.execute(
            "INSERT INTO users (id, name, email) VALUES (?, ?, ?)",
            (user.user_id, user.name, user.email)
        )
        conn.commit()
        conn.close()

# Controller to handle UI input and call the use case
from use_cases.create_user_use_case import CreateUserUseCase

class UserController:
    def __init__(self):
        # Inject the concrete repository into the use case
        self.create_user_use_case = CreateUserUseCase(SqlUserRepository())

    def handle_submission(self, form_data: dict) -> str:
        try:
            created_user = self.create_user_use_case.execute(form_data)
            return f"Success! Created user: {created_user.name}"
        except ValueError as e:
            return f"Error: {str(e)}"

Frameworks & Drivers Layer (Console UI)

from interface_adapters.user_controller import UserController

def run_console_ui():
    controller = UserController()
    print("Create a new user:")
    name = input("Enter name: ")
    email = input("Enter email: ")
    result = controller.handle_submission({"name": name, "email": email})
    print(result)

if __name__ == "__main__":
    run_console_ui()

Key Takeaways

  • The boundary interface (UserRepository) is the critical link: it's defined in the inner Use Cases layer, so the inner layer never depends on outer-layer code.
  • The flow stays true to the rule: inner layers never know about outer layers—the Use Case only calls an interface, and the actual persistence logic is implemented in an outer layer that depends on the inner interface.
  • If you later switch from SQL to MongoDB, or from a console UI to a web form, you only need to change the outer layers—your core business logic (Entities and Use Cases) stays untouched.

内容的提问来源于stack exchange,提问作者Thieri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:36:14