Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ Simple web client: [MediCare UI](https://medicare-z79v.onrender.com)

### Architecture

- [System flow](docs/foundation-flow.md) — domain model, core flows, and simple business rules.
- [System flow](docs/system-flow.md) — domain model, core flows, and simple business rules.
- [DTOs and MapStruct mappers](docs/dtos-and-mappers.md) — request/read DTOs, mappers, facade, and build notes.

### API documentation

**Official reference (not tied to your laptop)** — Start with [system flow](docs/foundation-flow.md): domain model, HTTP behaviour, status codes, and business rules. That document is the main human-readable contract for the API.
- **Official reference** — [System flow](docs/system-flow.md) is the main human-readable contract: domain model, HTTP behaviour, status codes, and business rules. It is not tied to a particular environment.

**OpenAPI (machine-readable, generated)** — The running service exposes OpenAPI 3 JSON at `/v3/api-docs` (see `springdoc.api-docs.path` in `application.properties`). For the deployed API, use `https://medicare-z79v.onrender.com/v3/api-docs`. For a server you start on your machine, use `http://localhost:8080/v3/api-docs`.
- **OpenAPI** — The running app serves generated OpenAPI 3 JSON at `/v3/api-docs` (see `springdoc.api-docs.path` in `application.properties`). Use [Deployed OpenAPI](https://medicare-z79v.onrender.com/v3/api-docs) for production, or [Local OpenAPI](http://localhost:8080/v3/api-docs) when the app runs on your machine.

**Interactive Swagger UI** — Same paths as above, with `/swagger-ui.html` (see `springdoc.swagger-ui.path`). Production: `https://medicare-z79v.onrender.com/swagger-ui.html`. **Local development only:** `http://localhost:8080/swagger-ui.html` when you are running the app locally; it is not the canonical place to read “official” docs, only a convenient explorer.
- **Interactive Swagger UI** — Explorer at `/swagger-ui.html` (see `springdoc.swagger-ui.path` in `application.properties`). Use [Deployed Swagger UI](https://medicare-z79v.onrender.com/swagger-ui.html) in production, or [Local Swagger UI](http://localhost:8080/swagger-ui.html) for a quick local try-out (not a substitute for the official reference).

- [Bruno collection](bruno/)runnable HTTP requests for the REST API, grouped by resource. Open the `bruno` folder in [Bruno](https://www.usebruno.com/), then pick an environment: **local** (`environments/local.bru`, `http://localhost:8080`) or **prod** (`environments/prod.bru`, `https://medicare-z79v.onrender.com`, same host as [MediCare UI](#live-deployment)).
- **Bruno collection**The [Bruno](https://www.usebruno.com/) collection lives under [bruno/](bruno/): runnable requests grouped by resource. Pick an environment in Bruno: **local** uses [local.bru](bruno/environments/local.bru) with base URL [Local API](http://localhost:8080); **prod** uses [prod.bru](bruno/environments/prod.bru) with base URL [Deployed API](https://medicare-z79v.onrender.com) (same host as [MediCare UI](#live-deployment)).
5 changes: 1 addition & 4 deletions docs/dtos-and-mappers.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,10 +236,7 @@ MapStruct and Lombok annotation processing are enabled in `pom.xml`. Unmapped ta

## Possible next steps

- REST controllers and `@ControllerAdvice` for consistent error bodies
- Map domain exceptions to `ApiResponse` error shapes
- Pagination metadata on list endpoints
- Auditing fields (`createdAt`, `updatedAt`) on entities and DTOs where needed
- Read-side caching where response shapes are stable

For domain rules and flows, see [System flow](foundation-flow.md).
For domain rules and flows, see [System flow](system-flow.md).
169 changes: 0 additions & 169 deletions docs/foundation-flow.md

This file was deleted.

191 changes: 191 additions & 0 deletions docs/system-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
# MediCare — System Flow

This document is the human-readable contract for **MediCare**: domain model, HTTP behaviour, main flows, and business rules the application enforces today.

---

## 1. Purpose

MediCare is a small clinic appointment system. A clinic registers **patients** and **doctors**, books **appointments** between them, and can cancel or complete visits while a few scheduling rules are enforced.

---

## 2. Domain model

The domain has three JPA entities, one supporting enum, and a shared persistence base.

### 2.1 Auditing and identifiers

All entities extend **`BaseEntity`** (`@MappedSuperclass`): **`id`** (UUID, JPA-generated), **`createdAt`** / **`updatedAt`** (`OffsetDateTime`, Hibernate `@CreationTimestamp` / `@UpdateTimestamp`). Read-side DTOs expose the same audit fields via **`BaseDto`**.

### 2.2 Entity–relationship diagram

```mermaid
erDiagram
PATIENT ||--o{ APPOINTMENT : "books"
DOCTOR ||--o{ APPOINTMENT : "serves"
DOCTOR }o--|| SPECIALTY : "has"

PATIENT {
UUID id
OffsetDateTime createdAt
OffsetDateTime updatedAt
String fullName
String phoneNumber
}

DOCTOR {
UUID id
OffsetDateTime createdAt
OffsetDateTime updatedAt
String fullName
Specialty specialty
}

APPOINTMENT {
UUID id
OffsetDateTime createdAt
OffsetDateTime updatedAt
OffsetDateTime appointmentTime
AppointmentStatus status
}

SPECIALTY {
enum CARDIOLOGY
enum DENTIST
enum THERAPIST
}
```

### 2.3 Relationships

- One **Doctor** has at most **one Specialty** (enum); it may be unset until assigned via the API.
- One **Patient** can have **many Appointments**.
- One **Doctor** can have **many Appointments**.
- One **Appointment** belongs to exactly **one Patient** and **one Doctor**.

### 2.4 Appointment status

```mermaid
stateDiagram-v2
[*] --> SCHEDULED : book appointment
SCHEDULED --> CANCELLED : cancel
SCHEDULED --> COMPLETED : visit finished
CANCELLED --> [*]
COMPLETED --> [*]
```

- `SCHEDULED` — booked, not yet completed or cancelled.
- `CANCELLED` — cancelled; **terminal**.
- `COMPLETED` — visit finished; **terminal**.

---

## 3. HTTP surface (summary)

All REST paths are under **`/v1`**. Successful and error bodies use the **`ApiResponse`** envelope (status, message, optional `data`, optional `errorCode`, **`OffsetDateTime` timestamp**).

| Concern | Method | Path |
|--------|--------|------|
| List patients | `GET` | `/v1/patients` |
| Create patient | `POST` | `/v1/patients` |
| Delete patient | `DELETE` | `/v1/patients/{id}` |
| Patient’s appointments | `GET` | `/v1/patients/{id}/appointments` |
| List doctors | `GET` | `/v1/doctors` |
| Create doctor | `POST` | `/v1/doctors` |
| Assign specialty | `PATCH` | `/v1/doctors/{id}/specialty` |
| Delete doctor | `DELETE` | `/v1/doctors/{id}` |
| Doctor’s appointments | `GET` | `/v1/doctors/{id}/appointments` |
| List appointments (clinic-wide) | `GET` | `/v1/appointments` |
| Book appointment | `POST` | `/v1/appointments` |
| Cancel appointment | `POST` | `/v1/appointments/{id}/cancel` |
| Complete appointment | `POST` | `/v1/appointments/{id}/complete` |

---

## 4. Core flows

### 4.1 Book an appointment

Most rules are enforced here. Request body includes **`patientId`**, **`doctorId`**, and **`appointmentTime`** as **`OffsetDateTime`**.

```mermaid
sequenceDiagram
actor Client
participant API as AppointmentRestAdapter
participant UC as BookAppointmentUseCase
participant Repo as Repositories

Client->>API: POST /v1/appointments
API->>UC: invoke request
UC->>Repo: load patient and doctor
UC->>UC: validate future time and doctor specialty
UC->>Repo: check duplicate scheduled slot
UC->>Repo: save appointment SCHEDULED
UC-->>API: ApiResponse 201 with AppointmentDTO
API-->>Client: 201 Created
```

### 4.2 Cancel an appointment

```mermaid
flowchart TD
A[Client POST cancel] --> B{Appointment exists?}
B -- No --> E[404 Not Found]
B -- Yes --> C{Status = SCHEDULED?}
C -- No --> F[409 Conflict]
C -- Yes --> D[Set CANCELLED]
D --> G[200 OK]
```

### 4.3 Complete an appointment

- **Endpoint:** `POST /v1/appointments/{id}/complete`
- Only **`SCHEDULED`** appointments may be completed; **`CANCELLED`** or **`COMPLETED`** yields **409 Conflict**.
- Success sets status to **`COMPLETED`**.

### 4.4 List and filter appointments

Optional query parameters (combined with **AND**): `status` (repeatable), `date` (`YYYY-MM-DD`, full calendar day in the **JVM default zone**), `from` / `to` (inclusive bounds on **`appointmentTime`** as **`OffsetDateTime`**).

| Parameter | Meaning |
|-----------|---------|
| `status` | Repeat for multiple values, e.g. `status=SCHEDULED&status=COMPLETED`. Omit to ignore. |
| `date` | Calendar day; matches `[startOfDay, nextDay)` in the default zone. |
| `from` | Inclusive lower bound, e.g. `2026-05-14T00:00:00Z`. |
| `to` | Inclusive upper bound; same style as `from`. |

If both `from` and `to` are present, `from` must not be after `to` (**400 Bad Request**).

- **`GET /v1/appointments`** — full **`AppointmentDTO`** (patient and doctor embedded), sorted by **`appointmentTime`** ascending.
- **`GET /v1/doctors/{id}/appointments`** — **`DoctorAppointmentDTO`** list (no nested doctor), same filters and sort.
- **`GET /v1/patients/{id}/appointments`** — **`PatientAppointmentDTO`** list (no nested patient), same filters and sort.

### 4.5 Delete patient or doctor

| Method | Path | Success | Missing entity |
|--------|------|---------|----------------|
| `DELETE` | `/v1/patients/{id}` | **200 OK**, message `Patient deleted successfully.` | **404** `Patient not found.` |
| `DELETE` | `/v1/doctors/{id}` | **200 OK**, message `Doctor deleted successfully.` | **404** `Doctor not found.` |

**Appointments:** deletes call **`AppointmentRepository.deleteByPatient_Id`** / **`deleteByDoctor_Id`** before removing the parent row so no orphan appointments remain.

---

## 5. Business rules

Invariants enforced in the application layer (storage-agnostic wording):

| # | Rule | Where enforced |
|---|------|----------------|
| 1 | A doctor cannot have another **SCHEDULED** appointment at the **same** **`appointmentTime`** | `BookAppointmentUseCase` |
| 2 | **`appointmentTime`** must be **strictly in the future** at booking (using the injected **`Clock`**) | `BookAppointmentUseCase` |
| 3 | Only **`SCHEDULED`** appointments may be **completed** | `CompleteAppointmentUseCase` |
| 4 | Only **`SCHEDULED`** appointments may be **cancelled** | `CancelAppointmentUseCase` |
| 5 | Booking requires the doctor to have a **non-null specialty** | `BookAppointmentUseCase` |
| 6 | Deleting a **patient** or **doctor** removes **all** of their appointments first | `DeletePatientUseCase`, `DeleteDoctorUseCase` |
| 7 | **Duplicate doctor full name** is rejected on create | `CreateDoctorUseCase` |

> Rule **#1** is “same instant for same doctor,” not a duration overlap. If visit lengths are introduced later, this should become a real interval overlap check.

---
Loading
Loading