CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl-labs/separation-of-concerns

Enforce strict three-layer architecture: thin HTTP routes, pure service logic with domain errors, isolated data access with dependency injection.

94

1.08x
Quality

93%

Does it follow best practices?

Impact

97%

1.08x

Average score across 5 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

task.mdevals/scenario-3/

Healthcare Platform — API Restructure

Problem/Feature Description

A healthcare platform currently has a single monolithic platformService.ts file that handles patient management, appointment scheduling, billing/invoicing, and authentication. The file has grown to over 800 lines and the team is struggling with merge conflicts and test failures that are hard to isolate. A new feature (telehealth video sessions) needs to be added, but the tech lead has refused to merge any new code into the god service.

You have been asked to restructure the backend into appropriately scoped services, and to scaffold out the new telehealth feature alongside the restructured services. You don't need to reimplement full business logic — but the structure must be in place so that other developers can fill in the implementations.

The existing monolithic service handles these concerns:

  • Patient profile CRUD (create, read, update, delete)
  • Appointment booking (schedule, cancel, reschedule)
  • Billing (generate invoice, process payment, get invoice history)
  • Authentication (login, logout, reset password)

The new feature to add:

  • Telehealth session management (create session, join session, end session)

Input Files

The following files are provided as inputs. Extract them before beginning.

=============== FILE: inputs/platformService.ts =============== import { db } from './db'; import { sendEmail } from './email'; import { stripe } from './stripe'; import { jwt } from './jwt';

// GOD SERVICE - handles everything export class PlatformService { // --- Patient Management --- async createPatient(data: any) { const existing = await db.query('SELECT * FROM patients WHERE email = $1', [data.email]); if (existing) throw new Error('400: Patient already exists'); const patient = await db.query('INSERT INTO patients ...', [data.name, data.email, data.dob]); await sendEmail(data.email, 'Welcome to the platform'); return patient; }

async getPatient(id: string) { const row = await db.query('SELECT * FROM patients WHERE id = $1', [id]); if (!row) throw new Error('404: Patient not found'); return row; }

async updatePatient(id: string, data: any) { const row = await db.query('UPDATE patients SET ...', [data, id]); return row; }

// --- Appointment Booking --- async scheduleAppointment(patientId: string, doctorId: string, time: Date) { const conflicts = await db.query('SELECT * FROM appointments WHERE doctor_id = $1 AND time = $2', [doctorId, time]); if (conflicts.length > 0) throw new Error('422: Time slot not available'); const appt = await db.query('INSERT INTO appointments ...', [patientId, doctorId, time]); await sendEmail('doctor@example.com', 'New appointment scheduled'); return appt; }

async cancelAppointment(appointmentId: string) { await db.query('DELETE FROM appointments WHERE id = $1', [appointmentId]); await sendEmail('patient@example.com', 'Appointment cancelled'); }

// --- Billing --- async generateInvoice(patientId: string, items: any[]) { const total = items.reduce((sum, i) => sum + i.price, 0); const invoice = await db.query('INSERT INTO invoices ...', [patientId, items, total]); return invoice; }

async processPayment(invoiceId: string, paymentMethod: string) { const invoice = await db.query('SELECT * FROM invoices WHERE id = $1', [invoiceId]); if (!invoice) throw new Error('404: Invoice not found'); const charge = await stripe.charge(invoice.total, paymentMethod); await db.query('UPDATE invoices SET paid = true ...', [invoiceId, charge.id]); await sendEmail('patient@example.com', 'Payment received'); return charge; }

// --- Authentication --- async login(email: string, password: string) { const user = await db.query('SELECT * FROM users WHERE email = $1', [email]); if (!user) throw new Error('401: Invalid credentials'); const token = jwt.sign({ userId: user.id }); return { token }; }

async resetPassword(email: string) { const user = await db.query('SELECT * FROM users WHERE email = $1', [email]); if (!user) throw new Error('404: User not found'); const token = jwt.sign({ reset: true, userId: user.id }, '1h'); await sendEmail(email, Reset link: /reset?token=${token}); } }

Output Specification

Restructure the platform service into focused services following a clean three-layer architecture. Produce:

  • All service files in src/services/ — one per domain area (existing) plus the new telehealth service
  • Corresponding repository files in src/repositories/ for each domain area that needs data access
  • Route files in src/routes/ for each domain area
  • src/errors/domain.ts — domain error types
  • restructure-plan.md — brief notes on how you split the god service and what each new service is responsible for

Stub out method bodies (throw new Error('not implemented') is fine) — the focus is on correct structure, layer separation, and appropriate scoping of each service.

evals

tile.json