Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Windows Tipps & SecurityNighthawk M7 Pro im Test: Flexibler, aber teurer 5G-Router(21.09.2026 um 10:30 Uhr)
Sichere ProgrammierungNeue Gmail-Funktion: So sparst du jetzt Zeit bei Einmalcodes(21.09.2026 um 10:00 Uhr)
Sichere ProgrammierungYour GIF exporter is fine — the container is the problem(21.09.2026 um 10:01 Uhr)
Sichere ProgrammierungCSS, Motion, or GSAP? I Choose by Who Owns the Animation(21.09.2026 um 10:12 Uhr)
Windows Tipps & SecurityNighthawk M7 Pro im Test: Flexibler, aber teurer 5G-Router(21.09.2026 um 10:30 Uhr)
Sichere ProgrammierungNeue Gmail-Funktion: So sparst du jetzt Zeit bei Einmalcodes(21.09.2026 um 10:00 Uhr)
Sichere ProgrammierungYour GIF exporter is fine — the container is the problem(21.09.2026 um 10:01 Uhr)
Sichere ProgrammierungCSS, Motion, or GSAP? I Choose by Who Owns the Animation(21.09.2026 um 10:12 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Monolith vs Modular Monolith vs Multi-Module vs Microservices in Spring Boot

🏗 Monolith vs Modular Monolith vs Multi-Module vs Microservices in Spring Boot A Complete Beginner-Friendly Guide with Real Hospital System Example 📚 Introduction Building a backend system with Spring Boot is exciting, but every developer f…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

🏗 Monolith vs Modular Monolith vs Multi-Module vs Microservices in Spring Boot

A Complete Beginner-Friendly Guide with Real Hospital System Example

📚 Introduction

Building a backend system with Spring Boot is exciting, but every developer faces the same crucial question:



"Which architecture style should I choose for my project?"



The answer isn't always straightforward. Should you build everything in one place? Split into modules? Go fully distributed with microservices?



In this comprehensive guide, we'll explore four different architecture styles using a real-world example:



🏥 Hospital OPD Appointment & Billing System

Our system will handle:



Identity Module - Login, registration, user management



Doctor Management - Doctor profiles, schedules, specializations



Appointment Booking - Slot booking, availability checking



Billing - Invoice generation, payment processing



Notification - SMS/Email alerts for appointments



We'll implement this same system in four different ways and understand:



How each architecture works



How modules communicate



Step-by-step implementation details



Pros and cons of each approach



When to use which architecture



🎯 Understanding the Problem Domain

Before diving into architectures, let's understand our hospital system requirements:



java

// Core business requirements:




  • Patients can register and login

  • Patients can view available doctors

  • Patients can book appointments

  • System sends confirmation notifications

  • Billing generates invoice after appointment

  • Doctors can manage their schedules
    Each module has specific responsibilities, and they need to work together seamlessly.



1️⃣ MONOLITH ARCHITECTURE

The Traditional Approach

🔍 What is a Monolith?

A monolithic architecture means everything lives inside ONE Spring Boot application. Think of it as a single, unified codebase where all modules - Identity, Doctor, Appointment, Billing, and Notification - coexist in the same project.



🏢 Real-Life Analogy

Imagine a single hospital building where:



Reception desk (Identity)



Doctor's chambers (Doctor Management)



Appointment desk (Booking)



Billing counter (Billing)



Communication center (Notification)



All departments are in the same building. Staff can walk between departments easily. That's your monolith!



📦 Detailed Project Structure

text

opd-system/

├── src/

│ ├── main/

│ │ ├── java/

│ │ │ └── com/

│ │ │ └── hospital/

│ │ │ └── opd/

│ │ │ ├── OpdApplication.java

│ │ │ │

│ │ │ ├── controller/

│ │ │ │ ├── AuthController.java

│ │ │ │ ├── DoctorController.java

│ │ │ │ ├── AppointmentController.java

│ │ │ │ ├── BillingController.java

│ │ │ │ └── NotificationController.java

│ │ │ │

│ │ │ ├── service/

│ │ │ │ ├── UserService.java

│ │ │ │ ├── DoctorService.java

│ │ │ │ ├── AppointmentService.java

│ │ │ │ ├── BillingService.java

│ │ │ │ └── NotificationService.java

│ │ │ │

│ │ │ ├── repository/

│ │ │ │ ├── UserRepository.java

│ │ │ │ ├── DoctorRepository.java

│ │ │ │ ├── AppointmentRepository.java

│ │ │ │ ├── BillingRepository.java

│ │ │ │ └── NotificationRepository.java

│ │ │ │

│ │ │ ├── entity/

│ │ │ │ ├── User.java

│ │ │ │ ├── Doctor.java

│ │ │ │ ├── Appointment.java

│ │ │ │ ├── Bill.java

│ │ │ │ └── Notification.java

│ │ │ │

│ │ │ └── config/

│ │ │ ├── SecurityConfig.java

│ │ │ └── SwaggerConfig.java

│ │ │

│ │ └── resources/

│ │ ├── application.properties

│ │ └── db/

│ │ └── migration/

│ │

│ └── test/

│ └── java/

│ └── com/

│ └── hospital/

│ └── opd/

│ ├── service/

│ └── controller/

🔄 How Modules Communicate in Monolith

In a monolith, communication is direct and simple - just Java method calls!



Example: Booking an Appointment



java

@Service

public class AppointmentService {



@Autowired
private DoctorService doctorService;

@Autowired
private UserService userService;

@Autowired
private NotificationService notificationService;

@Autowired
private BillingService billingService;

@Transactional
public Appointment bookAppointment(AppointmentRequest request) {
// 1. Verify doctor availability (direct method call)
Doctor doctor = doctorService.findAvailableDoctor(
request.getDoctorId(),
request.getDateTime()
);

// 2. Get patient details (direct method call)
User patient = userService.findById(request.getPatientId());

// 3. Create appointment
Appointment appointment = new Appointment();
appointment.setDoctor(doctor);
appointment.setPatient(patient);
appointment.setDateTime(request.getDateTime());
appointment.setStatus(Status.CONFIRMED);

appointment = appointmentRepository.save(appointment);

// 4. Generate bill (direct method call)
Bill bill = billingService.createBill(appointment);

// 5. Send notification (direct method call)
notificationService.sendAppointmentConfirmation(
patient.getEmail(),
appointment
);

return appointment;
}



}

Notice: No HTTP calls, no message queues, no network communication. Just plain Java method calls within the same JVM.



🧩 Architecture Diagram

text

┌─────────────────────────────────────┐

│ OPD MONOLITH APP │

│ (Single JVM) │

│ │

│ ┌─────────────────────────────────┐ │

│ │ CONTROLLER LAYER │ │

│ │ Auth Doctor Appt Bill Notif│ │

│ └───────────────┬─────────────────┘ │

│ │ │

│ ┌───────────────▼─────────────────┐ │

│ │ SERVICE LAYER │ │

│ │ Auth Doctor Appt Bill Notif│ │

│ └───────────────┬─────────────────┘ │

│ │ │

│ ┌───────────────▼─────────────────┐ │

│ │ REPOSITORY LAYER │ │

│ │ Auth Doctor Appt Bill Notif│ │

│ └───────────────┬─────────────────┘ │

│ │ │

│ ┌───────────────▼─────────────────┐ │

│ │ ENTITY LAYER │ │

│ │ User Doctor Appt Bill Notif│ │

│ └─────────────────────────────────┘ │

└─────────────────┬───────────────────┘



┌─────────▼─────────┐

│ PostgreSQL DB │

│ (Single Schema) │

└───────────────────┘

💻 Code Walkthrough: Monolith Implementation

Application Properties:



properties





application.properties



spring.datasource.url=jdbc:postgresql://localhost:5432/opd_db

spring.datasource.username=admin

spring.datasource.password=password

spring.jpa.hibernate.ddl-auto=update

spring.jpa.show-sql=true

Entity Example:



java

@entity

@Table(name = "appointments")

public class Appointment {

@id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;



@ManyToOne
@JoinColumn(name = "doctor_id")
private Doctor doctor;

@ManyToOne
@JoinColumn(name = "patient_id")
private User patient;

private LocalDateTime appointmentDateTime;
private String status;

@OneToOne(mappedBy = "appointment")
private Bill bill;

// getters, setters, constructors



}

✅ Pros of Monolith

Simple to Develop



One codebase to manage



Easy to understand for beginners



Quick setup and configuration



Easy Debugging



Single log file



No network issues to debug



Clear stack traces



Fast Communication



Method calls are instant



No network latency



No serialization overhead



Simple Deployment



One JAR/WAR file



One server to configure



Easy to deploy anywhere



Perfect for MVP



Build fast



Validate ideas quickly



Iterate rapidly



❌ Cons of Monolith

Scaling Limitations



java

// Can't scale just the appointment module

// Must scale entire application

Code Organization Challenges



java

// As code grows, dependencies become messy

// Service classes might accidentally use each other's internals

Team Development Bottlenecks



Multiple teams working on same codebase



Merge conflicts common



Feature coordination overhead



Technology Lock-in



One technology stack for everything



Can't use different databases for different modules



One Failure Affects All



If billing has memory leak, appointment booking fails too



No fault isolation



🎯 When to Use Monolith

✅ Perfect for:



Startups building MVP



Small teams (2-5 developers)



Simple applications with low complexity



Learning projects



Internal tools



Projects with tight deadlines



❌ Avoid when:



Multiple teams are working independently



You need to scale specific features



System has high complexity



You need technology diversity



2️⃣ MODULAR MONOLITH (Spring Modulith)

Structured Single Application

🔍 What is a Modular Monolith?

A Modular Monolith (or Modulith) is still ONE application, but internally divided into strict modules with clear boundaries. Each module has its own public API and hides its internal implementation.



🏢 Real-Life Analogy

Think of a modern hospital with secured departments:



Each department (Identity, Doctor, Appointment) has its own entrance



Departments communicate through formal channels



Staff can't just walk into any department without proper authorization



Each department has its own internal operations that others can't see



📦 Detailed Project Structure with Spring Modulith

text

opd-modulith/

├── src/

│ └── main/

│ └── java/

│ └── com/

│ └── hospital/

│ └── opd/

│ ├── OpdApplication.java

│ │

│ ├── identity/

│ │ ├── api/

│ │ │ ├── IdentityService.java (public interface)

│ │ │ └── dto/

│ │ │ ├── UserDTO.java

│ │ │ └── LoginRequest.java

│ │ ├── internal/

│ │ │ ├── model/

│ │ │ │ └── User.java (internal entity)

│ │ │ ├── repository/

│ │ │ │ └── UserRepository.java

│ │ │ └── service/

│ │ │ └── IdentityServiceImpl.java

│ │ └── events/

│ │ └── UserRegisteredEvent.java

│ │

│ ├── doctor/

│ │ ├── api/

│ │ │ ├── DoctorService.java

│ │ │ └── dto/

│ │ │ ├── DoctorDTO.java

│ │ │ └── AvailabilityDTO.java

│ │ ├── internal/

│ │ │ ├── model/

│ │ │ │ └── Doctor.java

│ │ │ ├── repository/

│ │ │ │ └── DoctorRepository.java

│ │ │ └── service/

│ │ │ └── DoctorServiceImpl.java

│ │ └── events/

│ │ └── DoctorAvailabilityChangedEvent.java

│ │

│ ├── appointment/

│ │ ├── api/

│ │ │ ├── AppointmentService.java

│ │ │ └── dto/

│ │ │ ├── AppointmentRequest.java

│ │ │ └── AppointmentDTO.java

│ │ ├── internal/

│ │ │ ├── model/

│ │ │ │ └── Appointment.java

│ │ │ ├── repository/

│ │ │ │ └── AppointmentRepository.java

│ │ │ └── service/

│ │ │ └── AppointmentServiceImpl.java

│ │ └── events/

│ │ └── AppointmentBookedEvent.java

│ │

│ ├── billing/

│ │ ├── api/

│ │ │ ├── BillingService.java

│ │ │ └── dto/

│ │ │ └── InvoiceDTO.java

│ │ ├── internal/

│ │ │ ├── model/

│ │ │ │ └── Bill.java

│ │ │ ├── repository/

│ │ │ │ └── BillingRepository.java

│ │ │ └── service/

│ │ │ └── BillingServiceImpl.java

│ │ └── events/

│ │ └── BillGeneratedEvent.java

│ │

│ └── notification/

│ ├── api/

│ │ ├── NotificationService.java

│ │ └── dto/

│ │ └── NotificationRequest.java

│ ├── internal/

│ │ ├── model/

│ │ │ └── Notification.java

│ │ ├── repository/

│ │ │ └── NotificationRepository.java

│ │ └── service/

│ │ └── NotificationServiceImpl.java

│ └── events/

│ └── NotificationSentEvent.java

🔄 Communication in Modular Monolith

Spring Modulith supports two communication patterns:



Pattern 1: Controlled Direct Calls (via Public APIs)

java

// Identity Module - Public API

package com.hospital.opd.identity.api;



public interface IdentityService {

UserDTO findUserById(Long id);

UserDTO registerUser(RegisterRequest request);

boolean validateUserCredentials(String email, String password);

}



// Identity Module - Internal Implementation

package com.hospital.opd.identity.internal.service;



@Service

@Modulithic(module = "identity")

class IdentityServiceImpl implements IdentityService {

@Autowired

private UserRepository userRepository;



@Override
public UserDTO findUserById(Long id) {
// Internal implementation hidden from other modules
User user = userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
return mapToDTO(user);
}

private UserDTO mapToDTO(User user) {
// Internal mapping logic
return new UserDTO(user.getId(), user.getName(), user.getEmail());
}



}



// Appointment Module using Identity Module

package com.hospital.opd.appointment.api;



@Service

@Modulithic(module = "appointment")

class AppointmentServiceImpl implements AppointmentService {



// Using public API, not internal implementation
@Autowired
private IdentityService identityService;

@Override
public AppointmentDTO bookAppointment(AppointmentRequest request) {
// Can only access public methods
UserDTO patient = identityService.findUserById(request.getPatientId());

// Cannot access internal Identity classes!
// identityService.userRepository.findById() ❌ Not accessible

// Rest of the logic
Appointment appointment = createAppointment(patient, request);
return mapToDTO(appointment);
}



}

Pattern 2: Event-Based Communication (Recommended)

java

// 1. Define Event in Appointment Module

package com.hospital.opd.appointment.events;



public class AppointmentBookedEvent {

private final Long appointmentId;

private final Long patientId;

private final Long doctorId;

private final LocalDateTime appointmentTime;

private final BigDecimal consultationFee;



// constructor, getters



}



// 2. Publish Event in Appointment Module

package com.hospital.opd.appointment.internal.service;



@Service

@Modulithic(module = "appointment")

class AppointmentServiceImpl implements AppointmentService {



@Autowired
private ApplicationEventPublisher eventPublisher;

@Override
@Transactional
public AppointmentDTO bookAppointment(AppointmentRequest request) {
// 1. Create appointment
Appointment appointment = createAppointment(request);

// 2. Save to database
appointment = appointmentRepository.save(appointment);

// 3. Publish event - other modules will react
eventPublisher.publishEvent(new AppointmentBookedEvent(
appointment.getId(),
appointment.getPatientId(),
appointment.getDoctorId(),
appointment.getDateTime(),
appointment.getConsultationFee()
));

return mapToDTO(appointment);
}



}



// 3. Billing Module Listens to Event

package com.hospital.opd.billing.internal.service;



@Service

@Modulithic(module = "billing")

class BillingEventListener {



@Autowired
private BillingService billingService;

@EventListener
@Transactional
public void handleAppointmentBooked(AppointmentBookedEvent event) {
// Billing module reacts independently
InvoiceDTO invoice = billingService.createInvoice(
event.getAppointmentId(),
event.getPatientId(),
event.getConsultationFee()
);

// Can publish its own events
eventPublisher.publishEvent(new BillGeneratedEvent(invoice));
}



}



// 4. Notification Module Listens to Multiple Events

package com.hospital.opd.notification.internal.service;



@Service

@Modulithic(module = "notification")

class NotificationEventListener {



@Autowired
private NotificationService notificationService;

@EventListener
public void handleAppointmentBooked(AppointmentBookedEvent event) {
notificationService.sendAppointmentConfirmation(
event.getPatientId(),
event.getAppointmentId()
);
}

@EventListener
public void handleBillGenerated(BillGeneratedEvent event) {
notificationService.sendInvoiceNotification(
event.getPatientId(),
event.getInvoiceNumber()
);
}



}

🧩 Spring Modulith Architecture Diagram

text

┌─────────────────────────────────────────┐

│ MODULAR MONOLITH │

│ (Single Application) │

│ │

│ ┌─────────────────────────────────────┐ │

│ │ API GATEWAY LAYER │ │

│ │ (All REST Controllers) │ │

│ └─────────────────────────────────────┘ │

│ │ │

│ ┌──────────────────▼──────────────────┐ │

│ │ MODULE BOUNDARIES │ │

│ │ ┌───────────────┐ ┌──────────────┐ │ │

│ │ │ IDENTITY │ │ DOCTOR │ │ │

│ │ │ MODULE │ │ MODULE │ │ │

│ │ │ ┌─────────┐ │ │ ┌─────────┐ │ │ │

│ │ │ │ PUBLIC │ │ │ │ PUBLIC │ │ │ │

│ │ │ │ API │ │ │ │ API │ │ │ │

│ │ │ └────┬────┘ │ │ └────┬────┘ │ │ │

│ │ │ ┌────▼────┐ │ │ ┌────▼────┐ │ │ │

│ │ │ │INTERNAL │ │ │ │INTERNAL │ │ │ │

│ │ │ │ LOGIC │ │ │ │ LOGIC │ │ │ │

│ │ │ └─────────┘ │ │ └─────────┘ │ │ │

│ │ └────────────────┘ └──────────────┘ │ │

│ │ │ │

│ │ ┌────────────────┐ ┌──────────────┐ │ │

│ │ │ APPOINTMENT │ │ BILLING │ │ │

│ │ │ MODULE │ │ MODULE │ │ │

│ │ │ ┌─────────┐ │ │ ┌─────────┐ │ │ │

│ │ │ │ PUBLIC │ │ │ │ PUBLIC │ │ │ │

│ │ │ │ API │ │ │ │ API │ │ │ │

│ │ │ └────┬────┘ │ │ └────┬────┘ │ │ │

│ │ │ ┌────▼────┐ │ │ ┌────▼────┐ │ │ │

│ │ │ │INTERNAL │ │ │ │INTERNAL │ │ │ │

│ │ │ │ LOGIC │ │ │ │ LOGIC │ │ │ │

│ │ │ └─────────┘ │ │ └─────────┘ │ │ │

│ │ └────────────────┘ └──────────────┘ │ │

│ │ │ │

│ │ ┌────────────────────┐ │ │

│ │ │ NOTIFICATION │ │ │

│ │ │ MODULE │ │ │

│ │ │ ┌───────────────┐ │ │ │

│ │ │ │ PUBLIC │ │ │ │

│ │ │ │ API │ │ │ │

│ │ │ └───────┬───────┘ │ │ │

│ │ │ ┌───────▼───────┐ │ │ │

│ │ │ │ INTERNAL │ │ │ │

│ │ │ │ LOGIC │ │ │ │

│ │ │ └───────────────┘ │ │ │

│ │ └────────────────────┘ │ │

│ └──────────────────▲───────────────────┘ │

│ │ │

│ ┌──────┴──────┐ │

│ │ EVENT BUS │ │

│ │ (In-Memory) │ │

│ └─────────────┘ │

└─────────────────────┬───────────────────────┘



┌─────────▼─────────┐

│ PostgreSQL DB │

│ (Single Schema) │

└───────────────────┘

🛠️ Spring Modulith Configuration

Add Modulith Dependency:



xml



org.springframework.modulith

spring-modulith-starter-core

1.1.0



Module Validation:



java

@SpringBootApplication

public class OpdApplication {

public static void main(String[] args) {

SpringApplication.run(OpdApplication.class, args);

}



// Verify module boundaries at startup
@Bean
ApplicationModuleInitializer moduleInitializer() {
return () -> {
var modules = ApplicationModules.of(OpdApplication.class);
modules.verify();
System.out.println("✅ Module boundaries verified!");
};
}



}

Module Documentation:



java

@org.springframework.modulith.ApplicationModule(

allowedDependencies = {"identity", "doctor"}

)

package com.hospital.opd.appointment;



// This module can only depend on identity and doctor modules

✅ Pros of Modular Monolith

Clean Architecture



Clear separation of concerns



Enforced module boundaries



No accidental dependencies



Better Code Organization



java

// Each module owns its domain

// Internal implementation can change without affecting others

Easier Testing



java

@ModuleTest

class AppointmentModuleTest {

// Test module in isolation

// Mock external module interactions

}

Future-Proof



Easy to extract modules into microservices later



Already has clear boundaries



No Network Overhead



Still fast in-JVM communication



Events are lightweight



❌ Cons of Modular Monolith

Single Deployment Unit



Still deploy everything together



Can't scale modules independently



Single Database



All modules share one database



Database becomes bottleneck



Learning Curve



Developers must understand module boundaries



Need discipline to maintain separation



🎯 When to Use Modular Monolith

✅ Perfect for:



Growing products with increasing complexity



Medium-sized teams (5-15 developers)



Systems preparing for microservices migration



Projects needing clear architecture



Most real-world business applications



❌ Avoid when:



You need independent scaling



Each module requires different databases



Teams are fully independent geographically



👉 This is the sweet spot for most applications!



3️⃣ MULTI-MODULE ARCHITECTURE

Build-Time Modularization

🔍 What is Multi-Module Architecture?

Multi-module architecture uses Maven or Gradle to split code into separate modules at build time. Each module is a separate Maven/Gradle subproject, but they all combine into ONE final application.



🏢 Real-Life Analogy

Think of a hospital built from prefabricated sections:



Each department (Identity, Doctor, Appointment) is built separately



They're designed to fit together perfectly



Once assembled, they form one complete hospital



You can't move a department after construction



📦 Detailed Multi-Module Project Structure

text

opd-multi-module/

├── pom.xml (parent pom)

├── identity-module/

│ ├── pom.xml

│ └── src/

│ └── main/

│ └── java/

│ └── com/

│ └── hospital/

│ └── identity/

│ ├── IdentityApplication.java

│ ├── controller/

│ ├── service/

│ ├── repository/

│ ├── model/

│ └── config/

├── doctor-module/

│ ├── pom.xml

│ └── src/

│ └── main/

│ └── java/

│ └── com/

│ └── hospital/

│ └── doctor/

│ ├── DoctorApplication.java

│ ├── controller/

│ ├── service/

│ ├── repository/

│ ├── model/

│ └── config/

├── appointment-module/

│ ├── pom.xml

│ └── src/

│ └── main/

│ └── java/

│ └── com/

│ └── hospital/

│ └── appointment/

│ ├── AppointmentApplication.java

│ ├── controller/

│ ├── service/

│ ├── repository/

│ ├── model/

│ └── config/

├── billing-module/

│ ├── pom.xml

│ └── src/

│ └── main/

│ └── java/

│ └── com/

│ └── hospital/

│ └── billing/

│ ├── BillingApplication.java

│ ├── controller/

│ ├── service/

│ ├── repository/

│ ├── model/

│ └── config/

├── notification-module/

│ ├── pom.xml

│ └── src/

│ └── main/

│ └── java/

│ └── com/

│ └── hospital/

│ └── notification/

│ ├── NotificationApplication.java

│ ├── controller/

│ ├── service/

│ ├── repository/

│ ├── model/

│ └── config/

└── application/

├── pom.xml

└── src/

└── main/

└── java/

└── com/

└── hospital/

└── app/

├── HospitalApplication.java

└── config/

└── ModuleConfiguration.java

📄 Maven Configuration Files

Parent pom.xml:



xml

<?xml version="1.0" encoding="UTF-8"?>



4.0.0



<groupId>com.hospital</groupId>
<artifactId>opd-multi-module</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>

<modules>
<module>identity-module</module>
<module>doctor-module</module>
<module>appointment-module</module>
<module>billing-module</module>
<module>notification-module</module>
<module>application</module>
</modules>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.5</version>
</parent>

<properties>
<java.version>17</java.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.hospital</groupId>
<artifactId>identity-module</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.hospital</groupId>
<artifactId>doctor-module</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Other module dependencies -->
</dependencies>
</dependencyManagement>





Module pom.xml (appointment-module):



xml

<?xml version="1.0" encoding="UTF-8"?>



4.0.0



<parent>
<groupId>com.hospital</groupId>
<artifactId>opd-multi-module</artifactId>
<version>1.0.0</version>
</parent>

<artifactId>appointment-module</artifactId>
<packaging>jar</packaging>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- Dependencies on other modules -->
<dependency>
<groupId>com.hospital</groupId>
<artifactId>doctor-module</artifactId>
</dependency>
<dependency>
<groupId>com.hospital</groupId>
<artifactId>identity-module</artifactId>
</dependency>
</dependencies>





Application Module pom.xml:



xml

<?xml version="1.0" encoding="UTF-8"?>



4.0.0



<parent>
<groupId>com.hospital</groupId>
<artifactId>opd-multi-module</artifactId>
<version>1.0.0</version>
</parent>

<artifactId>application</artifactId>
<packaging>jar</packaging>

<dependencies>
<!-- Include all modules -->
<dependency>
<groupId>com.hospital</groupId>
<artifactId>identity-module</artifactId>
</dependency>
<dependency>
<groupId>com.hospital</groupId>
<artifactId>doctor-module</artifactId>
</dependency>
<dependency>
<groupId>com.hospital</groupId>
<artifactId>appointment-module</artifactId>
</dependency>
<dependency>
<groupId>com.hospital</groupId>
<artifactId>billing-module</artifactId>
</dependency>
<dependency>
<groupId>com.hospital</groupId>
<artifactId>notification-module</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.hospital.app.HospitalApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>





🔄 How Modules Communicate in Multi-Module

Communication is through compile-time dependencies and direct method calls.



java

// Appointment Module Service

package com.hospital.appointment.service;



@Service

public class AppointmentService {



// Direct dependency on other modules
private final DoctorService doctorService;
private final IdentityService identityService;
private final BillingService billingService;

public AppointmentService(
DoctorService doctorService,
IdentityService identityService,
BillingService billingService) {
this.doctorService = doctorService;
this.identityService = identityService;
this.billingService = billingService;
}

@Transactional
public AppointmentDTO bookAppointment(AppointmentRequest request) {
// Direct calls to other modules' services
DoctorDTO doctor = doctorService.findAvailableDoctor(
request.getDoctorId(),
request.getDateTime()
);

UserDTO patient = identityService.findById(request.getPatientId());

Appointment appointment = new Appointment();
appointment.setDoctorId(doctor.getId());
appointment.setPatientId(patient.getId());
appointment.setDateTime(request.getDateTime());
appointment.setStatus("BOOKED");

appointment = appointmentRepository.save(appointment);

// Call billing module directly
InvoiceDTO invoice = billingService.createInvoice(
appointment.getId(),
patient.getId(),
doctor.getConsultationFee()
);

return mapToDTO(appointment);
}



}

🧩 Multi-Module Architecture Diagram

text

┌──────────────────────────────────────────┐

│ MULTI-MODULE PROJECT │

│ (Maven/Gradle Parent) │

│ │

│ ┌─────────────────────────────────────┐ │

│ │ COMPILE TIME │ │

│ │ DEPENDENCY TREE │ │

│ │ │ │

│ │ identity-module.jar │ │

│ │ ▲ │ │

│ │ │ depends │ │

│ │ doctor-module.jar │ │

│ │ ▲ │ │

│ │ │ depends │ │

│ │ appointment-module.jar ◄──┐ │ │

│ │ ▲ │ │ │

│ │ │ depends │ │ │

│ │ billing-module.jar ─────────┘ │ │

│ │ ▲ │ │

│ │ │ depends │ │

│ │ notification-module.jar │ │

│ │ ▲ │ │

│ │ │ depends │ │

│ │ application.jar │ │

│ │ (Final Assembly) │ │

│ └─────────────────────────────────────┘ │

│ │ │

│ ┌──────────────────▼──────────────────┐ │

│ │ RUNTIME (Single JVM) │ │

│ │ │ │

│ │ ┌───────────────────────────┐ │ │

│ │ │ ONE SPRING BOOT APP │ │ │

│ │ │ with all modules │ │ │

│ │ │ loaded together │ │ │

│ │ └───────────────────────────┘ │ │

│ │ │ │ │

│ │ ┌────────┴────────┐ │ │

│ │ │ PostgreSQL DB │ │ │

│ │ │ (Single Schema) │ │ │

│ │ └─────────────────┘ │ │

│ └─────────────────────────────────────┘ │

└──────────────────────────────────────────┘

💡 Key Differences from Modular Monolith

java

// In Multi-Module: Compile-time dependencies

// appointment-module/pom.xml explicitly declares:



com.hospital

doctor-module



// In Modular Monolith: Runtime module boundaries

// No explicit dependency declaration needed in pom.xml

// Boundaries are enforced by package structure and Spring Modulith

✅ Pros of Multi-Module

Clear Build Dependencies



xml





com.hospital

billing-module



Parallel Development



Teams can work on different modules



Clear ownership



Independent versioning



Reusable Modules



xml





com.hospital

notification-module

1.0.0



Faster Builds



Maven/Gradle incremental builds



Only rebuild changed modules



Better IDE Support



IntelliJ/Eclipse understand module structure



Easy navigation between modules



❌ Cons of Multi-Module

Tight Coupling at Runtime



java

// Even though modules are separate at build time,

// at runtime they're all in one JVM with no boundaries

No Runtime Isolation



One module's memory leak affects all



Can't restart just one module



Complex Refactoring



Moving code between modules requires dependency changes



Circular dependencies possible



🎯 When to Use Multi-Module

✅ Perfect for:



Large codebases needing organization



Multiple teams sharing code



Projects with reusable components



Enterprise internal systems



Libraries and frameworks



❌ Avoid when:



You need runtime isolation



Modules need independent scaling



You want strict runtime boundaries



4️⃣ MICROSERVICES ARCHITECTURE

Fully Distributed System

🔍 What are Microservices?

Microservices architecture splits the application into completely independent services. Each service:



Is its own Spring Boot application



Has its own database



Can be deployed independently



Communicates over network



🏢 Real-Life Analogy

Think of a hospital campus with separate buildings:



Identity building (separate structure)



Doctor's office building



Appointment center



Billing department building



Notification center



Each building has:



Its own staff



Its own resources



Its own phone system



Communication happens via phones/couriers (network)



📦 Microservices Project Structure

text

hospital-system/

├── identity-service/

│ ├── pom.xml

│ ├── src/

│ │ └── main/

│ │ ├── java/com/hospital/identity/

│ │ │ ├── IdentityServiceApplication.java

│ │ │ ├── controller/

│ │ │ ├── service/

│ │ │ ├── repository/

│ │ │ ├── model/

│ │ │ └── config/

│ │ └── resources/

│ │ ├── application.properties

│ │ └── db/migration/

│ └── Dockerfile



├── doctor-service/

│ ├── pom.xml

│ ├── src/

│ │ └── main/

│ │ ├── java/com/hospital/doctor/

│ │ │ ├── DoctorServiceApplication.java

│ │ │ ├── controller/

│ │ │ ├── service/

│ │ │ ├── repository/

│ │ │ ├── model/

│ │ │ └── client/

│ │ │ ├── IdentityServiceClient.java

│ │ │ └── AppointmentServiceClient.java

│ │ └── resources/

│ │ ├── application.properties

│ │ └── db/migration/

│ └── Dockerfile



├── appointment-service/

│ ├── pom.xml

│ ├── src/

│ │ └── main/

│ │ ├── java/com/hospital/appointment/

│ │ │ ├── AppointmentServiceApplication.java

│ │ │ ├── controller/

│ │ │ ├── service/

│ │ │ ├── repository/

│ │ │ ├── model/

│ │ │ └── client/

│ │ │ ├── DoctorServiceClient.java

│ │ │ └── IdentityServiceClient.java

│ │ └── resources/

│ │ ├── application.properties

│ │ └── db/migration/

│ └── Dockerfile



├── billing-service/

│ ├── pom.xml

│ ├── src/

│ │ └── main/

│ │ ├── java/com/hospital/billing/

│ │ │ ├── BillingServiceApplication.java

│ │ │ ├── controller/

│ │ │ ├── service/

│ │ │ ├── repository/

│ │ │ ├── model/

│ │ │ └── consumer/

│ │ │ └── AppointmentEventConsumer.java

│ │ └── resources/

│ │ ├── application.properties

│ │ └── db/migration/

│ └── Dockerfile



├── notification-service/

│ ├── pom.xml

│ ├── src/

│ │ └── main/

│ │ ├── java/com/hospital/notification/

│ │ │ ├── NotificationServiceApplication.java

│ │ │ ├── controller/

│ │ │ ├── service/

│ │ │ ├── repository/

│ │ │ ├── model/

│ │ │ └── consumer/

│ │ │ ├── AppointmentEventConsumer.java

│ │ │ └── BillingEventConsumer.java

│ │ └── resources/

│ │ ├── application.properties

│ │ └── db/migration/

│ └── Dockerfile



├── api-gateway/

│ ├── pom.xml

│ ├── src/

│ │ └── main/

│ │ ├── java/com/hospital/gateway/

│ │ │ ├── GatewayApplication.java

│ │ │ └── config/

│ │ │ ├── RouteConfig.java

│ │ │ └── SecurityConfig.java

│ │ └── resources/

│ │ └── application.properties

│ └── Dockerfile



├── service-registry/

│ ├── pom.xml

│ ├── src/

│ │ └── main/

│ │ ├── java/com/hospital/registry/

│ │ │ ├── RegistryApplication.java

│ │ │ └── config/

│ │ │ └── SecurityConfig.java

│ │ └── resources/

│ │ └── application.properties

│ └── Dockerfile



├── config-server/

│ ├── pom.xml

│ ├── src/

│ │ └── main/

│ │ ├── java/com/hospital/config/

│ │ │ └── ConfigServerApplication.java

│ │ └── resources/

│ │ └── application.properties

│ └── Dockerfile



├── docker-compose.yml

├── kubernetes/

│ ├── deployments/

│ └── services/

└── README.md

🔧 Infrastructure Components




  1. Service Registry (Eureka)
    java
    // service-registry/src/main/java/com/hospital/registry/RegistryApplication.java
    @SpringBootApplication
    @EnableEurekaServer
    public class RegistryApplication {
    public static void main(String[] args) {
    SpringApplication.run(RegistryApplication.class, args);
    }
    }
    properties
    # service-registry/src/main/resources/application.properties
    spring.application.name=service-registry
    server.port=8761
    eureka.client.register-with-eureka=false
    eureka.client.fetch-registry=false


  2. API Gateway (Spring Cloud Gateway)

    java

    // api-gateway/src/main/java/com/hospital/gateway/config/RouteConfig.java

    @Configuration

    public class RouteConfig {



    @bean

    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {

    return builder.routes()

    .route("identity-service", r -> r

    .path("/api/auth/", "/api/users/")

    .uri("lb://IDENTITY-SERVICE"))

    .route("doctor-service", r -> r

    .path("/api/doctors/")

    .uri("lb://DOCTOR-SERVICE"))

    .route("appointment-service", r -> r

    .path("/api/appointments/
    ")

    .uri("lb://APPOINTMENT-SERVICE"))

    .route("billing-service", r -> r

    .path("/api/bills/", "/api/invoices/")

    .uri("lb://BILLING-SERVICE"))

    .build();

    }



    @bean

    public GlobalFilter customGlobalFilter() {

    return (exchange, chain) -> {

    // Add correlation ID for tracing

    String correlationId = UUID.randomUUID().toString();

    exchange = exchange.mutate()

    .request(r -> r.header("X-Correlation-ID", correlationId))

    .build();

    return chain.filter(exchange);

    };

    }

    }

    🔄 Service Communication Patterns

    Pattern 1: Synchronous REST Communication

    java

    // doctor-service/src/main/java/com/hospital/doctor/client/IdentityServiceClient.java

    @FeignClient(name = "identity-service", url = "${identity.service.url}")

    public interface IdentityServiceClient {



    @GetMapping("/api/users/{userId}")

    UserDTO getUserById(@PathVariable("userId") Long userId);



    @GetMapping("/api/users/validate")

    Boolean validateUser(@RequestParam("email") String email,

    @RequestParam("password") String password);

    }





// appointment-service/src/main/java/com/hospital/appointment/client/DoctorServiceClient.java

@FeignClient(name = "doctor-service")

public interface DoctorServiceClient {




@GetMapping("/api/doctors/{doctorId}/availability")
AvailabilityDTO checkAvailability(
@PathVariable("doctorId") Long doctorId,
@RequestParam("dateTime") String dateTime
);

@PostMapping("/api/doctors/{doctorId}/book-slot")
void bookDoctorSlot(
@PathVariable("doctorId") Long doctorId,
@RequestBody SlotBookingRequest request
);




}



// appointment-service/src/main/java/com/hospital/appointment/service/AppointmentService.java

@Service

public class AppointmentService {




private final DoctorServiceClient doctorClient;
private final IdentityServiceClient identityClient;

public AppointmentDTO bookAppointment(AppointmentRequest request) {
// 1. Validate patient with identity service (REST call)
UserDTO patient = identityClient.getUserById(request.getPatientId());

// 2. Check doctor availability (REST call)
AvailabilityDTO availability = doctorClient.checkAvailability(
request.getDoctorId(),
request.getDateTime()
);

if (!availability.isAvailable()) {
throw new AppointmentNotAvailableException();
}

// 3. Book doctor slot (REST call)
doctorClient.bookDoctorSlot(
request.getDoctorId(),
new SlotBookingRequest(request.getDateTime(), request.getPatientId())
);

// 4. Create appointment locally
Appointment appointment = createAppointment(request, patient);
appointment = appointmentRepository.save(appointment);

return mapToDTO(appointment);
}




}

Pattern 2: Asynchronous Event Communication with Kafka

java

// 1. Define Common Event Classes (shared library)

// shared-events/src/main/java/com/hospital/events/AppointmentBookedEvent.java

public class AppointmentBookedEvent {

private Long appointmentId;

private Long patientId;

private Long doctorId;

private LocalDateTime appointmentTime;

private BigDecimal consultationFee;

private String patientEmail;

private String patientPhone;




// constructors, getters, setters




}



// 2. Appointment Service - Event Publisher

// appointment-service/src/main/java/com/hospital/appointment/service/AppointmentService.java

@Service

public class AppointmentService {




@Autowired
private KafkaTemplate<String, AppointmentBookedEvent> kafkaTemplate;

@Transactional
public AppointmentDTO bookAppointment(AppointmentRequest request) {
// Create appointment
Appointment appointment = createAppointment(request);
appointment = appointmentRepository.save(appointment);

// Publish event to Kafka
AppointmentBookedEvent event = new AppointmentBookedEvent(
appointment.getId(),
appointment.getPatientId(),
appointment.getDoctorId(),
appointment.getDateTime(),
appointment.getConsultationFee(),
appointment.getPatientEmail(),
appointment.getPatientPhone()
);

kafkaTemplate.send("appointment-events", event);

return mapToDTO(appointment);
}




}



// 3. Billing Service - Event Consumer

// billing-service/src/main/java/com/hospital/billing/consumer/AppointmentEventConsumer.java

@Service

public class AppointmentEventConsumer {




@Autowired
private BillingService billingService;

@KafkaListener(topics = "appointment-events", groupId = "billing-group")
public void handleAppointmentBooked(AppointmentBookedEvent event) {
log.info("Received appointment booked event: {}", event.getAppointmentId());

// Generate bill
Invoice invoice = billingService.generateInvoice(
event.getAppointmentId(),
event.getPatientId(),
event.getConsultationFee()
);

// Publish billing event for notification service
BillGeneratedEvent billEvent = new BillGeneratedEvent(
invoice.getId(),
event.getPatientId(),
invoice.getAmount(),
event.getPatientEmail(),
event.getPatientPhone()
);

kafkaTemplate.send("billing-events", billEvent);
}




}



// 4. Notification Service - Multiple Event Consumers

// notification-service/src/main/java/com/hospital/notification/consumer/EventConsumer.java

@Service

public class EventConsumer {




@Autowired
private NotificationService notificationService;

@KafkaListener(topics = "appointment-events", groupId = "notification-group")
public void handleAppointmentBooked(AppointmentBookedEvent event) {
notificationService.sendAppointmentConfirmation(
event.getPatientEmail(),
event.getPatientPhone(),
event.getAppointmentId(),
event.getAppointmentTime()
);
}

@KafkaListener(topics = "billing-events", groupId = "notification-group")
public void handleBillGenerated(BillGeneratedEvent event) {
notificationService.sendInvoiceNotification(
event.getPatientEmail(),
event.getPatientPhone(),
event.getInvoiceId(),
event.getAmount()
);
}




}

🧩 Microservices Architecture Diagram

text

┌─────────────────────────────────────────────────────────────────────┐

│ CLIENT APPLICATIONS │

│ (Web, Mobile, Third-party APIs) │

└────────────────────────────────────┬────────────────────────────────┘





┌─────────────────────────────────────────────────────────────────────┐

│ API GATEWAY │

│ (Spring Cloud Gateway) │

│ │

│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │

│ │ Routing │ │ Rate │ │ Security │ │

│ │ │ │ Limiting │ │ │ │

│ └──────────────┘ └──────────────┘ └──────────────┘ │

└────────────┬─────────────────────────────┬──────────────────────────┘

│ │

▼ ▼

┌────────────────────────┐ ┌────────────────────────┐

│ SERVICE REGISTRY │ │ CONFIG SERVER │

│ (Eureka Server) │◄────►│ (Spring Cloud Config)│

│ service discovery │ │ centralized config │

└────────────────────────┘ └────────────────────────┘




│ Service Registration





┌─────────────────────────────────────────────────────────────────────┐

│ MICROSERVICES │

│ │

│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │

│ │ IDENTITY │ │ DOCTOR │ │ APPOINTMENT │ │

│ │ SERVICE │ │ SERVICE │ │ SERVICE │ │

│ │ │ │ │ │ │ │

│ │ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │

│ │ │PostgreSQL│ │ │ │PostgreSQL│ │ │ │PostgreSQL│ │ │

│ │ │ DB 1 │ │ │ │ DB 2 │ │ │ │ DB 3 │ │ │

│ │ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │ │

│ └────────────────┘ └────────────────┘ └────────────────┘ │

│ │

│ ┌────────────────┐ ┌────────────────┐ │

│ │ BILLING │ │ NOTIFICATION │ │

│ │ SERVICE │ │ SERVICE │ │

│ │ │ │ │ │

│ │ ┌──────────┐ │ │ ┌──────────┐ │ │

│ │ │PostgreSQL│ │ │ │PostgreSQL│ │ │

│ │ │ DB 4 │ │ │ │ DB 5 │ │ │

│ │ └──────────┘ │ │ └──────────┘ │ │

│ └────────────────┘ └────────────────┘ │

└─────────────────────────────────────────────────────────────────────┘





┌─────────────────────────────────────────────────────────────────────┐

│ MESSAGE BROKER │

│ (Apache Kafka) │

│ │

│ ┌─────────────────────────────────────────────────────────────┐ │

│ │ Topics: appointment-events, billing-events, notification-events│

│ └─────────────────────────────────────────────────────────────┘ │

└─────────────────────────────────────────────────────────────────────┘





┌─────────────────────────────────────────────────────────────────────┐

│ MONITORING STACK │

│ │

│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │

│ │ Prometheus │ │ Grafana │ │ Zipkin │ │

│ │ Metrics │ │ Dashboard │ │ Tracing │ │

│ └──────────────┘ └──────────────┘ └──────────────┘ │

└─────────────────────────────────────────────────────────────────────┘

📝 Service Configuration

application.yml for Appointment Service:



yaml

spring:

application:

name: appointment-service



datasource:

url: jdbc:postgresql://localhost:5432/appointment_db

username: app_user

password: ${DB_PASSWORD}



jpa:

hibernate:

ddl-auto: validate

show-sql: false



kafka:

bootstrap-servers: localhost:9092

producer:

key-serializer: org.apache.kafka.common.serialization.StringSerializer

value-serializer: org.springframework.kafka.support.serializer.JsonSerializer

consumer:

group-id: appointment-group

key-deserializer: org.apache.kafka.common.serialization.StringDeserializer

value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer

properties:

spring.json.trusted.packages: "*"



eureka:

client:

service-url:

defaultZone: http://localhost:8761/eureka/

instance:

hostname: localhost

prefer-ip-address: true



server:

port: 8083



management:

endpoints:

web:

exposure:

include: health,info,metrics,prometheus

🐳 Docker Compose Configuration

yaml






docker-compose.yml



version: '3.8'



services:

# Databases

postgres-identity:

image: postgres:15

environment:

POSTGRES_DB: identity_db

POSTGRES_USER: identity_user

POSTGRES_PASSWORD: ${IDENTITY_DB_PASSWORD}

volumes:

- identity-data:/var/lib/postgresql/data

ports:

- "5432:5432"



postgres-doctor:

image: postgres:15

environment:

POSTGRES_DB: doctor_db

POSTGRES_USER: doctor_user

POSTGRES_PASSWORD: ${DOCTOR_DB_PASSWORD}

volumes:

- doctor-data:/var/lib/postgresql/data

ports:

- "5433:5432"



postgres-appointment:

image: postgres:15

environment:

POSTGRES_DB: appointment_db

POSTGRES_USER: appointment_user

POSTGRES_PASSWORD: ${APPOINTMENT_DB_PASSWORD}

volumes:

- appointment-data:/var/lib/postgresql/data

ports:

- "5434:5432"



# Message Broker

zookeeper:

image: confluentinc/cp-zookeeper:latest

environment:

ZOOKEEPER_CLIENT_PORT: 2181

ZOOKEEPER_TICK_TIME: 2000

ports:

- "2181:2181"



kafka:

image: confluentinc/cp-kafka:latest

depends_on:

- zookeeper

ports:

- "9092:9092"

environment:

KAFKA_BROKER_ID: 1

KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181

KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092

KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1



# Service Registry

service-registry:

build: ./service-registry

ports:

- "8761:8761"

environment:

- SPRING_PROFILES_ACTIVE=docker



# Config Server

config-server:

build: ./config-server

ports:

- "8888:8888"

environment:

- SPRING_PROFILES_ACTIVE=docker



# API Gateway

api-gateway:

build: ./api-gateway

ports:

- "8080:8080"

depends_on:

- service-registry

- config-server

environment:

- SPRING_PROFILES_ACTIVE=docker

- EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://service-registry:8761/eureka/



# Microservices

identity-service:

build: ./identity-service

depends_on:

- postgres-identity

- service-registry

- config-server

- kafka

environment:

- SPRING_PROFILES_ACTIVE=docker

- DB_PASSWORD=${IDENTITY_DB_PASSWORD}

- EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://service-registry:8761/eureka/

- SPRING_KAFKA_BOOTSTRAPSERVERS=kafka:9092



doctor-service:

build: ./doctor-service

depends_on:

- postgres-doctor

- service-registry

- config-server

- kafka

environment:

- SPRING_PROFILES_ACTIVE=docker

- DB_PASSWORD=${DOCTOR_DB_PASSWORD}

- EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://service-registry:8761/eureka/

- SPRING_KAFKA_BOOTSTRAPSERVERS=kafka:9092



# ... similar for other services



volumes:

identity-data:

doctor-data:

appointment-data:

billing-data:

notification-data:

✅ Pros of Microservices

Independent Scaling



yaml






Scale only appointment service during peak hours



docker-compose up -d --scale appointment-service=5

Technology Diversity



java

// Appointment service can use PostgreSQL

// Notification service can use MongoDB for logs

// Billing service can use Oracle for compliance

Independent Deployment



Deploy billing service without touching others



Zero downtime deployments possible



A/B testing per service



Fault Isolation



java

// If billing service crashes, appointments still work

// Circuit breakers prevent cascading failures

@CircuitBreaker(name = "billing-service")

public Invoice createBill(Appointment appointment) {

return billingClient.createBill(appointment);

}

Team Autonomy



Each team owns their service



Independent release cycles



Choose their own tools



❌ Cons of Microservices

Distributed Systems Complexity



java

// Network calls can fail

// Need retry logic, circuit breakers

// Distributed tracing required

Data Consistency Challenges



java

// No ACID transactions across services

// Must implement Saga pattern

// Eventual consistency only

Network Latency



java

// Previously: method call (1ms)

// Now: REST call (50-100ms)

// Need to optimize API design

Debugging Difficulty



Logs spread across services



Need centralized logging



Correlation IDs essential



Operational Overhead



Need DevOps team



Monitoring required



Complex deployment pipelines



🎯 When to Use Microservices

✅ Perfect for:



Large enterprises with many teams



Systems needing independent scaling



Companies with DevOps maturity



Applications with millions of users



When different modules need different tech stacks



❌ Avoid when:



Small team (< 10 developers)



Simple application



Limited DevOps resources



Startup/MVP phase



Strict data consistency requirements



🚀 Recommended Evolution Path

Most successful systems evolve naturally:



text

Phase 1: MONOLITH



Phase 2: MODULAR MONOLITH



Phase 3: EXTRACT HIGH-LOAD MODULES



Phase 4: MICROSERVICES

Real-World Evolution Example

Year 1-2: Monolith



java

// Fast development, validate business model

// Hospital OPD system as single Spring Boot app

// Perfect for initial launch

Year 2-3: Modular Monolith



java

// Business growing, team expanding

// Introduce Spring Modulith

// Clear boundaries, event-driven communication

// Still one deployment

Year 3-4: Extract Billing Module



java

// Billing has high load, compliance requirements

// Extract as separate microservice

// Others remain modular monolith

// Hybrid architecture

Year 4+: Full Microservices



java

// Different teams for each domain

// Independent scaling needs

// Full microservices with Kubernetes

📊 Final Comparison Table

Feature Monolith Modular Monolith Multi-Module Microservices

Deployment Units 1 1 1 Multiple

Databases 1 shared 1 shared 1 shared Separate per service

Communication In-JVM method calls Method calls + Events Compile-time deps + method calls HTTP/REST + Message Queue

Scalability Scale entire app Scale entire app Scale entire app Scale per service

Development Speed (Initial) 🚀 Fast 🚀 Fast 🚀 Fast 🐢 Slow

Development Speed (Long-term) 🐢 Slow 🚀 Fast 🚀 Fast 🚀 Fast

Team Autonomy Low Medium Medium High

Testing Complexity Low Medium Medium High

Operational Complexity Low Low Low High

Fault Isolation None Limited None Excellent

Technology Diversity None None None Full

Learning Curve Easy Medium Medium Steep

Best For MVP, Startups Growing products Large codebases Enterprise scale

🧠 Final Advice for Junior Developers

📝 Golden Rules

Don't Start with Microservices!



java

// Bad: Starting new project with 10 microservices

// Good: Start simple, evolve when needed

Build Clean Boundaries from Day 1



java

// Even in monolith, think in modules

// Separate concerns, avoid circular dependencies

Understand Your Domain First



java

// Domain-driven design helps

// Bounded contexts become service boundaries later

Choose Based on Team Size



text

1-5 developers → Monolith or Modular Monolith

5-15 developers → Modular Monolith

15+ developers → Consider microservices

Remember: Amazon, Netflix Started as Monoliths



They evolved to microservices over years



They had the team size and expertise



They had clear business reasons



🎓 Career Advice

Learn Monolith first - Understand the basics



Master Modular Monolith - Most common in real jobs



Understand Multi-Module - For build management



Study Microservices - For enterprise roles



💡 The Sweet Spot

For 80% of real-world applications, a Modular Monolith is the perfect choice:



Clear architecture



Fast development



Easy deployment



Future-proof



No distributed complexity



📚 Summary

We've explored four architectures for the same Hospital OPD system:



Architecture One-Line Summary

Monolith Everything in one place, simple but messy

Modular Monolith One app with strict internal boundaries

Multi-Module Build-time separation, runtime together

Microservices Fully distributed, maximum flexibility

Your choice depends on:



Team size and expertise



Current stage of project



Scaling requirements



Organizational maturity



🎯 Final Thought

"The best architecture is not the most advanced one, but the one that solves your current problems without creating new ones."



Start simple, evolve gradually, and always keep your modules cleanly separated. Your future self will thank you!



Happy Coding! 🚀



Found this guide helpful? Share it with fellow developers!

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Monolith vs Modular Monolith vs Multi-Module vs Microservices in Spring Boot

Thematisch verwandte Begriffe: Monolith, Modular, MultiModule, Microservices · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-94036 | A security flaw has been discovered in D-Link DIR-X1860 and DIR-X1860Z u…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick