Production Architecture

You will understand how EngineerPrep's backend pieces — the Spring Boot services, the LLM client, pgvector search, and the lesson pipeline — are arranged so each one can do its job without breaking the others.

Why This Matters

It's a Tuesday afternoon. You open a pull request for a new EngineerPrep feature: the AI tutor should now stream answers back to the learner instead of waiting for the full response. The code looks clean. Tests pass. You ship it. Fifteen minutes later, the on-call alert fires. Latency on every endpoint has spiked — not just the tutor. The lesson generator is slow. Search is slow. Even the login page feels sluggish. Your first instinct: 'I must have introduced a bug.' You roll back the change. Latency stays high. The real culprit? Your streaming feature was holding each request thread open for 30 seconds waiting on Amazon Bedrock. EngineerPrep's Spring Boot service has a fixed-size thread pool. With all threads occupied waiting on Bedrock responses, every other feature — search, lesson generation, authentication — was stuck waiting for a free thread. No bug.…

The Simple Idea

Think of a busy restaurant kitchen. There's a host who takes orders, a prep cook who handles vegetables, a line cook who does the actual cooking, and an expediter who checks each plate before it goes out. Each person has one job. They hand work to the next person in line. If the line cook gets overwhelmed, only the line cook slows down — the host can still seat guests and take orders. Production architecture is the same idea applied to software. You split a system into components — each component has one clear responsibility. Components talk to each other through well-defined interfaces (the equivalent of calling out 'order up!'). No component reaches directly into another component's internals. In EngineerPrep, the components are: - RestController — the host. Accepts HTTP requests from the browser. - Service layer — the line cook.…

See It in Action

Step 1 — The request arrives. A learner types 'What is a deadlock?' into the EngineerPrep interface and presses Send. An HTTP POST lands on the Spring Boot server. The TutorController — a @RestController bean — receives it. At this point, nothing has touched the database or the LLM. The controller's only job is to accept the request and hand it off. Step 2 — The controller calls the service. The controller passes the question text to TutorService . This is where the decision-making lives: should we look up existing lesson content first? Should we search for related questions? The service is the traffic director. It doesn't know SQL. It doesn't know how to call Bedrock. It just knows what needs to happen . Step 3 — The service asks two specialists in sequence.…

A Quick Example

We'll trace the tutor question flow from controller to service to LLM call. Each class does exactly one thing, and every dependency arrives through the constructor — Spring wires them together automatically. The Controller receives the HTTP request and immediately delegates. It holds no logic. The @PostMapping method takes a TutorRequest (a simple record with the learner's question) and returns a TutorResponse . It calls tutorService.answer() and hands back whatever the service returns. That's it — four lines of real work. The Service is where the decisions live. It calls the vector search first, then passes the results into the LLM call as context. Notice it never constructs a SQL string and never builds an HTTP request to Bedrock — it delegates both of those to specialists. If the vector search returns nothing (maybe the question is brand new), the service still proceeds…