Appointment Scheduling

Part B: Appointment Scheduling - Mediator Pattern

Document: Part B Analysis

Pattern: Mediator Pattern

Module: Appointment Scheduling System

Author: Ishara Lakshitha


Table of Contents


1. Overview & Problem Statement

Healthcare Domain Problems Addressed

Problem 1: Complex Scheduling Logic

  • Multiple entities involved: Patients, Doctors, Appointments, Schedules
  • Complex business rules for conflict detection
  • Time slot validation with 30-minute appointment windows
  • Integration with database persistence layer

Problem 2: Tight Coupling Between Components

  • Without mediation, each component would need direct references to others
  • Patient scheduling would require knowledge of Doctor availability
  • Doctor objects would need to manage their own appointment conflicts
  • Database operations scattered across multiple classes

Problem 3: Centralized Business Rule Management

  • Appointment validation rules need consistent application
  • Conflict detection algorithms require centralized logic
  • Status management and workflow coordination
  • Error handling and feedback messaging

Solution Approach

The Mediator Pattern implementation centralizes all complex scheduling interactions through the AppointmentScheduler class, which acts as the mediator between:

  • Patient booking requests
  • Doctor availability checking
  • Appointment conflict detection
  • Database persistence operations

2. Mediator Pattern Implementation

2.1 Pattern Participants

Mediator Interface (Implicit)

// The mediator responsibilities are fulfilled by AppointmentScheduler
// Could be made explicit with an interface, but concrete implementation suffices
public interface SchedulingMediator {
    String bookAppointment(String patientId, Doctor doctor,
                          LocalDateTime requestedDateTime, String reason, String doctorNotes);
}
 

Concrete Mediator

/**
 * The Mediator. It handles the complex logic of scheduling,
 * checking for conflicts, and coordinating between the database and the request.
 */
public class AppointmentScheduler {
    private final SchedulingDAO schedulingDAO;
 
    public AppointmentScheduler() {
        this.schedulingDAO = new SchedulingDAO();
    }
 
    /**
     * Central coordination method for appointment booking
     * Encapsulates all complex scheduling logic
     */
    public String bookAppointment(String patientId, Doctor doctor,
                                 LocalDateTime requestedDateTime, String reason,
                                 String doctorNotes) {
        // Step 1: Retrieve existing appointments for conflict checking
        List<Appointment> existingAppointments =
            schedulingDAO.getAppointmentsForDoctorOnDate(doctor.getDoctorId(),
                                                        requestedDateTime.toLocalDate());
 
        // Step 2: Apply business rules for conflict detection
        for (Appointment existing : existingAppointments) {
            LocalDateTime existingStart = existing.getAppointmentDateTime();
 
            // 30-minute conflict rule implementation
            if (requestedDateTime.isAfter(existingStart.minusMinutes(30)) &&
                requestedDateTime.isBefore(existingStart.plusMinutes(30))) {
                return "Booking failed: Time slot conflicts with an existing appointment (30-min rule).";
            }
        }
 
        // Step 3: Create and persist new appointment
        Appointment newAppointment = new Appointment(patientId, doctor.getDoctorId(),
                                                    requestedDateTime, reason);
        newAppointment.setDoctorNotes(doctorNotes);
 
        boolean success = schedulingDAO.createAppointment(newAppointment);
        return success ? "Appointment booked successfully!" :
                        "Booking failed: Could not save to database.";
    }
}
 

Colleague Classes

Appointment (Domain Entity)

public class Appointment implements Visitable {
    private int appointmentId;
    private String patientId;
    private String doctorId;
    private LocalDateTime appointmentDateTime;
    private String reason;
    private String status;
    private String doctorNotes;
    private LocalDateTime lastUpdated;
    private String lastUpdatedBy;
 
    public Appointment(String patientId, String doctorId,
                      LocalDateTime appointmentDateTime, String reason) {
        this.patientId = patientId;
        this.doctorId = doctorId;
        this.appointmentDateTime = appointmentDateTime;
        this.reason = reason;
        this.status = "Scheduled"; // Default status
        this.doctorNotes = "";
        this.lastUpdated = LocalDateTime.now();
        this.lastUpdatedBy = "system";
    }
 
    // Getters and setters...
}
 

Doctor (Domain Entity)

public class Doctor {
    private String doctorId;
    private String fullName;
    private String specialty;
 
    public Doctor(String doctorId, String fullName, String specialty) {
        this.doctorId = doctorId;
        this.fullName = fullName;
        this.specialty = specialty;
    }
 
    // Getters and setters...
}
 

SchedulingDAO (Data Access Layer)

public class SchedulingDAO {
    public List<Appointment> getAppointmentsForDoctorOnDate(String doctorId, LocalDate date) {
        // Database query implementation
    }
 
    public boolean createAppointment(Appointment appointment) {
        // Database persistence implementation
    }
 
    public List<Doctor> getAllDoctors() {
        // Doctor retrieval implementation
    }
}
 

3. UML Class Diagrams

3.0 Comprehensive Diagram

Part B - Appointment Scheduling - Mediator.png

(You can find the High Res images in the Github Project Repo)

3.1 Mediator Pattern Structure

┌─────────────────────────────────────┐
│      AppointmentController          │
│         (Client/Colleague)          │
├─────────────────────────────────────┤
│ - scheduler: AppointmentScheduler   │
│ - dao: SchedulingDAO                │
│ - view: AppointmentPanel            │
├─────────────────────────────────────┤
│ + bookNewAppointment(): void        │
│ + viewSchedule(): void              │
│ + cancelAppointment(): void         │
└─────────────────────────────────────┘

                    │ uses

┌─────────────────────────────────────┐
│     AppointmentScheduler            │
│         (Mediator)                  │
├─────────────────────────────────────┤
│ - schedulingDAO: SchedulingDAO      │
├─────────────────────────────────────┤
│ + bookAppointment(): String         │
└─────────────────────────────────────┘

                    │ coordinates

┌─────────────────────────────────────┐    ┌─────────────────────────────────────┐
│         Appointment                 │    │            Doctor                   │
│       (Colleague)                   │    │          (Colleague)                │
├─────────────────────────────────────┤    ├─────────────────────────────────────┤
│ - appointmentId: int                │    │ - doctorId: String                  │
│ - patientId: String                 │    │ - fullName: String                  │
│ - doctorId: String                  │    │ - specialty: String                 │
│ - appointmentDateTime: LocalDateTime│    ├─────────────────────────────────────┤
│ - reason: String                    │    │ + getDoctorId(): String             │
│ - status: String                    │    │ + getFullName(): String             │
│ - doctorNotes: String               │    │ + getSpecialty(): String            │
├─────────────────────────────────────┤    └─────────────────────────────────────┘
│ + getAppointmentDateTime(): LocalDT │
│ + setStatus(status): void           │
│ + setDoctorNotes(notes): void       │
└─────────────────────────────────────┘

                    │ manages

┌─────────────────────────────────────┐
│         SchedulingDAO               │
│       (Data Access)                 │
├─────────────────────────────────────┤
│ + getAppointmentsForDoctorOnDate()  │
│ + createAppointment(): boolean      │
│ + updateAppointment(): boolean      │
│ + getAllDoctors(): List<Doctor>     │
└─────────────────────────────────────┘

3.2 Interaction Flow Diagram

Client Request → Controller → Mediator → Database/Entities → Response

AppointmentController.bookNewAppointment()


AppointmentScheduler.bookAppointment()

            ├─→ SchedulingDAO.getAppointmentsForDoctorOnDate()
            │                    │
            │                    ▼
            │            [Conflict Detection Logic]
            │                    │
            ├─→ new Appointment()
            │                    │
            └─→ SchedulingDAO.createAppointment()


                         [Success/Failure Response]

4. Detailed Code Analysis

4.1 Conflict Detection Algorithm

/**
 * Core business logic: 30-minute appointment window conflict detection
 * This algorithm prevents overlapping appointments by ensuring a 30-minute
 * buffer around each existing appointment.
 */
public String bookAppointment(String patientId, Doctor doctor,
                             LocalDateTime requestedDateTime, String reason,
                             String doctorNotes) {
 
    // Retrieve all appointments for the doctor on the requested date
    List<Appointment> existingAppointments =
        schedulingDAO.getAppointmentsForDoctorOnDate(doctor.getDoctorId(),
                                                    requestedDateTime.toLocalDate());
 
    // Apply 30-minute conflict rule
    for (Appointment existing : existingAppointments) {
        LocalDateTime existingStart = existing.getAppointmentDateTime();
 
        /*
         * Conflict scenarios:
         * - Existing at 10:00, Requested at 10:15 → CONFLICT (within 30 min)
         * - Existing at 10:00, Requested at 09:45 → CONFLICT (within 30 min)
         * - Existing at 10:00, Requested at 10:30 → NO CONFLICT (exactly 30 min)
         * - Existing at 10:00, Requested at 09:30 → NO CONFLICT (exactly 30 min)
         */
        if (requestedDateTime.isAfter(existingStart.minusMinutes(30)) &&
            requestedDateTime.isBefore(existingStart.plusMinutes(30))) {
            return "Booking failed: Time slot conflicts with an existing appointment (30-min rule).";
        }
    }
 
    // No conflicts found - proceed with booking
    Appointment newAppointment = new Appointment(patientId, doctor.getDoctorId(),
                                                requestedDateTime, reason);
    newAppointment.setDoctorNotes(doctorNotes);
 
    boolean success = schedulingDAO.createAppointment(newAppointment);
    return success ? "Appointment booked successfully!" :
                    "Booking failed: Could not save to database.";
}
 

4.2 Data Access Coordination

/**
 * The mediator coordinates with the DAO layer to retrieve necessary data
 * for conflict checking without exposing database operations to the client
 */
public List<Appointment> getAppointmentsForDoctorOnDate(String doctorId, LocalDate date) {
    List<Appointment> appointments = new ArrayList<>();
    String sql = "SELECT * FROM appointments WHERE doctor_id = ? AND DATE(appointment_datetime) = ?";
 
    try (Connection conn = DatabaseManager.getConnection();
         PreparedStatement pstmt = conn.prepareStatement(sql)) {
 
        pstmt.setString(1, doctorId);
        pstmt.setDate(2, Date.valueOf(date));
        ResultSet rs = pstmt.executeQuery();
 
        while (rs.next()) {
            Appointment appt = new Appointment(
                rs.getString("patient_id"),
                rs.getString("doctor_id"),
                rs.getTimestamp("appointment_datetime").toLocalDateTime(),
                rs.getString("reason")
            );
            appt.setAppointmentId(rs.getInt("appointment_id"));
            appt.setStatus(rs.getString("status"));
            appt.setDoctorNotes(rs.getString("doctor_notes"));
            appointments.add(appt);
        }
    } catch (SQLException e) {
        System.err.println("Error fetching appointments for doctor " +
                          doctorId + " on " + date + ": " + e.getMessage());
    }
    return appointments;
}
 

4.3 Object Creation and State Management

/**
 * The mediator handles object creation and initial state setup
 * ensuring consistent object initialization across the system
 */
Appointment newAppointment = new Appointment(patientId, doctor.getDoctorId(),
                                           requestedDateTime, reason);
 
// Set additional properties through mediator logic
newAppointment.setDoctorNotes(doctorNotes);
// Status is automatically set to "Scheduled" in constructor
// lastUpdated and lastUpdatedBy are set automatically
 
// Persistence is handled through the mediator's DAO coordination
boolean success = schedulingDAO.createAppointment(newAppointment);
 

5. Complex Interaction Management

5.1 Multi-Step Workflow Coordination

The mediator manages a complex workflow that involves multiple steps:

/**
 * Complete appointment booking workflow managed by the mediator:
 * 1. Data Retrieval
 * 2. Business Rule Validation
 * 3. Object Creation
 * 4. Persistence
 * 5. Response Generation
 */
public String bookAppointment(...) {
    // Step 1: Data Retrieval
    List<Appointment> existingAppointments = schedulingDAO.getAppointmentsForDoctorOnDate(...);
 
    // Step 2: Business Rule Validation
    for (Appointment existing : existingAppointments) {
        if (hasConflict(requestedDateTime, existing.getAppointmentDateTime())) {
            return generateConflictMessage();
        }
    }
 
    // Step 3: Object Creation
    Appointment newAppointment = createAppointment(...);
 
    // Step 4: Persistence
    boolean success = schedulingDAO.createAppointment(newAppointment);
 
    // Step 5: Response Generation
    return generateResponseMessage(success);
}
 

5.2 Integration with Controller Layer

/**
 * AppointmentController delegates complex scheduling to the mediator
 * while handling UI concerns and user permissions
 */
public class AppointmentController {
    private final AppointmentScheduler scheduler; // Mediator instance
 
    private void bookNewAppointment() {
        // UI validation and data collection
        String patientId = view.patientIdField.getText().trim();
        String reason = view.reasonField.getText().trim();
        String doctorNotes = view.getDoctorNotesText();
 
        // Permission checking
        if (!currentUser.hasPermission("can_book_appointment")) {
            showPermissionError();
            return;
        }
 
        // Doctor selection logic
        Doctor selectedDoctor = getSelectedDoctor();
        LocalDateTime requestedDateTime = getRequestedDateTime();
 
        // Delegate to mediator for complex scheduling logic
        String resultMessage = scheduler.bookAppointment(
            patientId, selectedDoctor, requestedDateTime, reason, doctorNotes
        );
 
        // Handle response and update UI
        JOptionPane.showMessageDialog(view, resultMessage +
            "\\nBooked by: " + currentUser.getUsername());
        viewSchedule(); // Refresh display
        view.clearBookingFormFields();
    }
}
 

5.3 Database Transaction Coordination

/**
 * The mediator can coordinate database transactions across multiple operations
 * ensuring data consistency for complex scheduling scenarios
 */
public boolean createAppointment(Appointment appointment) {
    String sql = "INSERT INTO appointments (patient_id, doctor_id, appointment_datetime, " +
                "reason, doctor_notes, status) VALUES (?, ?, ?, ?, ?, ?)";
 
    try (Connection conn = DatabaseManager.getConnection();
         PreparedStatement pstmt = conn.prepareStatement(sql)) {
 
        pstmt.setString(1, appointment.getPatientId());
        pstmt.setString(2, appointment.getDoctorId());
        pstmt.setTimestamp(3, Timestamp.valueOf(appointment.getAppointmentDateTime()));
        pstmt.setString(4, appointment.getReason());
        pstmt.setString(5, appointment.getDoctorNotes());
        pstmt.setString(6, appointment.getStatus() != null ?
                           appointment.getStatus() : "Scheduled");
 
        boolean success = pstmt.executeUpdate() > 0;
        if (success) {
            System.out.println("SUCCESS: Created appointment for patient " +
                             appointment.getPatientId() + " with doctor " +
                             appointment.getDoctorId());
        }
        return success;
    } catch (SQLException e) {
        System.err.println("ERROR: Error creating appointment: " + e.getMessage());
        return false;
    }
}
 

6. Usage Scenarios

6.1 Scenario 1: Successful Appointment Booking

// Setup
AppointmentScheduler scheduler = new AppointmentScheduler();
Doctor doctor = new Doctor("D001", "Dr. Smith", "Cardiology");
LocalDateTime requestTime = LocalDateTime.of(2025, 8, 31, 14, 0); // 2:00 PM
 
// Execution
String result = scheduler.bookAppointment(
    "P001",           // Patient ID
    doctor,           // Doctor object
    requestTime,      // Requested time
    "Chest pain",     // Reason
    "Follow-up needed" // Doctor notes
);
 
// Expected Result
assertEquals("Appointment booked successfully!", result);
 

6.2 Scenario 2: Conflict Detection

// Setup - Existing appointment at 2:00 PM
Appointment existing = new Appointment("P002", "D001",
    LocalDateTime.of(2025, 8, 31, 14, 0), "Regular checkup");
// Assume existing appointment is already in database
 
// Attempt to book at 2:15 PM (within 30-minute window)
String result = scheduler.bookAppointment(
    "P003", doctor,
    LocalDateTime.of(2025, 8, 31, 14, 15),
    "Consultation", ""
);
 
// Expected Result
assertTrue(result.contains("conflicts with an existing appointment"));
 

6.3 Scenario 3: Multiple Doctor Coordination

// Different doctors can have appointments at the same time
Doctor doctor1 = new Doctor("D001", "Dr. Smith", "Cardiology");
Doctor doctor2 = new Doctor("D002", "Dr. Jones", "Neurology");
 
LocalDateTime sameTime = LocalDateTime.of(2025, 8, 31, 14, 0);
 
// Both should succeed - no conflict between different doctors
String result1 = scheduler.bookAppointment("P001", doctor1, sameTime, "Reason1", "");
String result2 = scheduler.bookAppointment("P002", doctor2, sameTime, "Reason2", "");
 
assertEquals("Appointment booked successfully!", result1);
assertEquals("Appointment booked successfully!", result2);
 

6.4 Scenario 4: Edge Case - Exact 30-Minute Boundary

// Existing appointment at 2:00 PM
LocalDateTime existingTime = LocalDateTime.of(2025, 8, 31, 14, 0);
// New appointment at 2:30 PM (exactly 30 minutes later)
LocalDateTime newTime = LocalDateTime.of(2025, 8, 31, 14, 30);
 
String result = scheduler.bookAppointment("P001", doctor, newTime, "Checkup", "");
 
// Should succeed - exactly 30 minutes is allowed
assertEquals("Appointment booked successfully!", result);
 

7. Benefits & Trade-offs

7.1 Mediator Pattern Benefits

Reduced Coupling

  • Colleague objects don't need direct references to each other
  • AppointmentController doesn't need to know about conflict detection logic
  • Doctor objects don't manage their own scheduling conflicts
  • Database operations are centralized and abstracted

Centralized Control

  • All scheduling business rules in one location
  • Consistent conflict detection across the application
  • Easy to modify scheduling algorithms without affecting multiple classes
  • Single point of control for appointment workflow

Simplified Object Protocols

  • Complex many-to-many interactions become one-to-many with mediator
  • Clear separation of concerns between UI, business logic, and data access
  • Standardized communication patterns through mediator interface

Enhanced Maintainability

  • Business rule changes require modifications in only one place
  • Easy to add new scheduling constraints or validation rules
  • Centralized error handling and logging
  • Clear audit trail for all scheduling operations

7.2 Specific Healthcare Domain Benefits

Patient Safety

  • Consistent conflict detection prevents double-booking
  • Centralized validation ensures all business rules are applied
  • Audit trail for all scheduling decisions

Operational Efficiency

  • Streamlined booking process through centralized coordination
  • Consistent error messages and user feedback
  • Scalable approach for complex scheduling scenarios

System Integration

  • Clean integration points with external systems
  • Centralized database transaction management
  • Consistent data validation and persistence

7.3 Trade-offs and Considerations

Mediator Complexity

  • The mediator can become complex as it centralizes all coordination logic
  • Risk of creating a "god object" if not properly designed
  • Need to balance centralization with appropriate delegation

Performance Considerations

  • Additional layer of indirection may impact performance
  • Database queries centralized through mediator may need optimization
  • Caching strategies may be needed for frequently accessed data

Testing Complexity

  • Mediator requires comprehensive testing of all interaction scenarios
  • Mock objects needed for testing isolated mediator behavior
  • Integration testing becomes critical for validating complete workflows

Single Point of Failure

  • Mediator becomes critical component - failures affect entire scheduling system
  • Need for robust error handling and recovery mechanisms
  • Monitoring and alerting for mediator health becomes important

8. Testing & Validation

8.1 Unit Testing the Mediator

@Test
public void testSuccessfulAppointmentBooking() {
    // Arrange
    AppointmentScheduler scheduler = new AppointmentScheduler();
    Doctor doctor = new Doctor("D001", "Dr. Test", "General");
    LocalDateTime appointmentTime = LocalDateTime.of(2025, 9, 1, 10, 0);
 
    // Act
    String result = scheduler.bookAppointment(
        "P001", doctor, appointmentTime, "Check-up", "Regular visit"
    );
 
    // Assert
    assertEquals("Appointment booked successfully!", result);
}
 
@Test
public void testConflictDetection() {
    // Arrange - Create existing appointment
    AppointmentScheduler scheduler = new AppointmentScheduler();
    Doctor doctor = new Doctor("D001", "Dr. Test", "General");
    LocalDateTime existingTime = LocalDateTime.of(2025, 9, 1, 10, 0);
    LocalDateTime conflictTime = LocalDateTime.of(2025, 9, 1, 10, 15);
 
    // Create first appointment
    scheduler.bookAppointment("P001", doctor, existingTime, "First", "");
 
    // Act - Try to book conflicting appointment
    String result = scheduler.bookAppointment("P002", doctor, conflictTime, "Second", "");
 
    // Assert
    assertTrue(result.contains("conflicts with an existing appointment"));
}
 
@Test
public void testBoundaryConditions() {
    AppointmentScheduler scheduler = new AppointmentScheduler();
    Doctor doctor = new Doctor("D001", "Dr. Test", "General");
 
    // Test exact 30-minute boundary
    LocalDateTime baseTime = LocalDateTime.of(2025, 9, 1, 10, 0);
    LocalDateTime boundaryTime = baseTime.plusMinutes(30);
 
    scheduler.bookAppointment("P001", doctor, baseTime, "First", "");
    String result = scheduler.bookAppointment("P002", doctor, boundaryTime, "Second", "");
 
    assertEquals("Appointment booked successfully!", result);
}
 

8.2 Integration Testing

@Test
public void testCompleteSchedulingWorkflow() {
    // Test complete workflow from controller through mediator to database
    AppointmentController controller = new AppointmentController(view, mainFrame, user);
 
    // Simulate user input
    view.patientIdField.setText("P001");
    view.reasonField.setText("Annual checkup");
 
    // Simulate doctor selection
    Doctor testDoctor = new Doctor("D001", "Dr. Test", "General");
    view.selectDoctor(testDoctor);
 
    // Simulate date/time selection
    view.setDateTime(LocalDateTime.of(2025, 9, 1, 14, 0));
 
    // Execute booking
    controller.bookNewAppointment();
 
    // Verify appointment was created in database
    List<Appointment> appointments = dao.getAppointmentsForDoctorOnDate(
        "D001", LocalDate.of(2025, 9, 1)
    );
    assertEquals(1, appointments.size());
    assertEquals("P001", appointments.get(0).getPatientId());
}
 

8.3 Performance Testing

@Test
public void testConcurrentBookingScenarios() {
    AppointmentScheduler scheduler = new AppointmentScheduler();
    Doctor doctor = new Doctor("D001", "Dr. Test", "General");
    ExecutorService executor = Executors.newFixedThreadPool(10);
 
    List<Future<String>> futures = new ArrayList<>();
 
    // Submit multiple concurrent booking requests
    for (int i = 0; i < 10; i++) {
        final int patientNum = i;
        Future<String> future = executor.submit(() -> {
            return scheduler.bookAppointment(
                "P" + String.format("%03d", patientNum),
                doctor,
                LocalDateTime.of(2025, 9, 1, 10, patientNum * 5), // 5-minute intervals
                "Test appointment " + patientNum,
                ""
            );
        });
        futures.add(future);
    }
 
    // Verify all bookings completed successfully
    for (Future<String> future : futures) {
        String result = future.get();
        assertEquals("Appointment booked successfully!", result);
    }
}
 

8.4 Error Handling Testing

@Test
public void testDatabaseFailureHandling() {
    // Test mediator behavior when database is unavailable
    AppointmentScheduler scheduler = new AppointmentScheduler();
 
    // Simulate database failure by using invalid connection
    // This would require dependency injection or mocking for proper testing
 
    String result = scheduler.bookAppointment(
        "P001", doctor, appointmentTime, "Test", ""
    );
 
    assertEquals("Booking failed: Could not save to database.", result);
}
 

Conclusion

The Mediator pattern implementation in the appointment scheduling system demonstrates a sophisticated approach to managing complex interactions in healthcare software. The AppointmentScheduler class effectively centralizes scheduling logic while maintaining loose coupling between system components.

Key Achievements

  1. Complexity Management: Successfully encapsulates complex scheduling business rules in a single, manageable component
  2. Conflict Resolution: Implements robust 30-minute conflict detection algorithm with clear boundary conditions
  3. Integration Coordination: Seamlessly coordinates between UI controllers, domain objects, and data access layers
  4. Maintainability: Provides a single point of control for scheduling modifications and enhancements
  5. Healthcare Compliance: Ensures consistent application of scheduling rules critical for patient care coordination

Real-World Impact

The mediator pattern proves particularly valuable in healthcare contexts where:

  • Patient Safety depends on accurate scheduling without conflicts
  • Operational Efficiency requires streamlined booking processes
  • System Integration needs clean interfaces between complex subsystems
  • Regulatory Compliance demands consistent business rule application

The implementation successfully addresses the challenge of managing complex interactions while maintaining clean architecture principles and providing a foundation for future enhancements to the scheduling system.


Document Status: Part B Complete

Next: Part C - Billing & Insurance Claims (Chain of Responsibility Pattern)