Consider a small repair-booking application. Customers choose a time slot, staff update bookings and the system sends a confirmation message. You can build those capabilities inside one deployable application or distribute them across independently deployed services. Both choices need clear rules about who can change a booking and what happens when confirmation fails.
For a first project maintained by a small team, a modular monolith is a reasonable starting hypothesis. That is an engineering judgment for this example, not a universal performance claim. Start with boundaries you can explain and one release process you can operate, then gather evidence before adding network boundaries.
Draw the business modules first
In one application, separate booking rules, staff access and notifications into modules. Give each a small interface. For example, a booking operation should not require a controller to assemble SQL statements for several unrelated modules. Keep the rule about available slots near the code that owns bookings.
One deployment does not require one enormous file. Conversely, creating several repositories does not make services independent if every change still requires coordinated releases. AWS's architecture comparison describes deployment and scaling differences. Apply those differences to the application's actual work instead of choosing by label.
Write a small dependency sketch in text:
Booking screen
calls Booking module
writes booking record
requests confirmation from Notification module
Now ask whether a notification failure should cancel the booking. For this fictional workshop project, choose to keep the booking and show confirmation pending. That decision creates a requirement for a retryable notification job. It is a product rule, and it exists whether the modules share a process or communicate over HTTP.
Understand what a service boundary adds
If notifications become a separate service, the request can time out even after that service accepts it. Messages can be delivered more than once. A deployment can leave callers and callees on different versions. Your contract needs a way to identify the booking, report acceptance and handle duplicate requests safely.
Microsoft's microservices architecture guide describes independent business capabilities and the extra challenges around communication, testing and data consistency. Service autonomy is useful when it solves an operational or organizational problem. It also means each service needs appropriate access controls, logs and a recovery procedure.
Data ownership is a useful test. If a notification service directly edits booking tables whenever it wants, the boundary is difficult to reason about. Instead, define which component owns booking status and how other components request a change. Microsoft's microservices architecture explanation discusses independent deployment and ownership of domain data.
Look for a specific reason to extract
Suppose the booking app is healthy but generating large confirmation documents causes measurable resource contention. A separate worker may help without converting every feature into a microservice. Or suppose an independent team needs to release notifications on its own schedule. That creates a different reason to consider a service boundary.
Record the evidence and the expected benefit. A service split should come with a falsifiable statement such as: booking requests will no longer wait for document rendering. Do not promise lower costs or faster delivery without measuring the actual system. More deployments can also mean more hosting, monitoring and on-call work.
If the difficulty is tangled code, extract an internal module first. Moving the same dependency tangle across HTTP can preserve the coupling while adding failure modes. A module boundary gives you a cheaper place to discover that the proposed interface asks for too much data.
Run a boundary exercise before building it
- Write a booking result containing a fictional booking id and confirmation status.
- Draft the request that would ask a notification component to send confirmation.
- Describe what happens if that request succeeds, times out or arrives twice.
- Identify which component may update each stored field.
- List how you would trace one booking across the proposed boundary.
The expected result is a small contract with an owner and a recovery action for every outcome. Use the JSON lab to inspect your example payload. The HTTP workbench can illustrate error categories with simulated lesson routes; it does not model a booking system or distributed transaction.
If you cannot describe duplicate handling or find the record owner, keep the boundary inside the application while you clarify the design. Revisit the deployment choice when a real workload or team requirement gives the proposed split a job to do.