Skip to content

Separation of Concerns

Principle: Break your application into components with clear responsibilities. Use constructor injection to make dependencies explicit and decouple components.

Example:

from wd.di import create_service_collection

# Create a service collection instance
sc = create_service_collection()

# EmailService: A service responsible for sending emails
@sc.singleton() # Registering EmailService as a singleton
class EmailService:
    def send_email(self, recipient: str, subject: str, body: str):
        print(f"Sending email to {recipient} with subject '{subject}' and body '{body}'")

# UserService: Depends on EmailService for notifications
@sc.singleton()
class UserService:
    def __init__(self, email_service: EmailService):
        self.email_service = email_service

    def notify(self, user_id: str, message: str):
        # In a real app, fetch user's email from a repository, etc.
        email_address = f"{user_id}@example.com" # Example email
        self.email_service.send_email(email_address, "Notification", message)

# Register and use the services:
provider = sc.build_service_provider()
user_service = provider.get_service(UserService)
user_service.notify("user123", "Your order has shipped!")

# Expected Output:
# Sending email to user123@example.com with subject 'Notification' and body 'Your order has shipped!'

When to use: Apply separation of concerns to keep business logic distinct from infrastructure (e.g., sending emails, logging), making your code easier to maintain and test.