from dataclasses import dataclass from datetime import datetime from abc import ABC, abstractmethod # Domain command + IDs used at the boundary of the application layer @dataclass class BookingId: value: str @dataclass class CreateBookingCommand: user_id: str departure: datetime origin: str destination: str # Application service interface class BookingApplicationService(ABC): @abstractmethod def create_booking(self, command: CreateBookingCommand) -> "Booking": pass @abstractmethod def get_booking(self, booking_id: BookingId) -> "Booking | None": pass class BookingApplicationServiceImpl(BookingApplicationService): def __init__( self, bookings: "BookingRepository", payments: "PaymentAdapter", ): self._bookings = bookings self._payments = payments def create_booking(self, command: CreateBookingCommand) -> "Booking": # Validation & business rules # (no HTTP, no SDK types) self._validate_command(command) provisional = Booking.provisional( user_id=command.user_id, departure=command.departure, origin=command.origin, destination=command.destination, ) # Orchestration via adapters/repositories payment_result = self._payments.charge( PaymentRequest.for_booking(provisional), ) confirmed = provisional.confirm(payment_result) self._bookings.save(confirmed) return confirmed def get_booking(self, booking_id: BookingId) -> "Booking | None": return self._bookings.find_by_id(booking_id) def _validate_command(self, command: CreateBookingCommand) -> None: # Pure validation; raise domain-level errors only pass