skip to content

In the GRASP set of design principles, what does the Controller principle say about who should receive a system input event (such as a button click or an incoming HTTP request), and why?

level: juniorimportance: must knowfreq 72%

answer

  1. first non-UI receiver of a system event
  2. facade controller vs use-case controller
  3. coordinate + delegate, never compute
  4. UI knows controller; controller never knows UI
  5. testable without a screen

basics

~20 s

GRASP Controller says: don't let the UI widget itself do the work. Give the event to a separate non-UI object — a controller — that coordinates the domain objects and returns a result. This keeps screen code and business rules apart.

solid answer

~50 s

GRASP (General Responsibility Assignment Software Patterns) Controller answers: which object should be the first non-UI receiver of a system event? Answer: a dedicated coordinator object that represents either the overall system/subsystem (a facade controller) or a specific use case/session (a use-case controller). The UI element — a button handler, an HTTP endpoint, a CLI parser — only translates the raw input into a call on that controller, then renders whatever comes back. Benefits: domain logic becomes reusable across UIs (web, mobile, batch, tests); it can be unit-tested without a screen or a servlet container; the use case gets one visible entry point where transactions, authorization and validation orchestration can live. The controller itself should not implement the business rules — it delegates to domain objects and services. When it starts implementing them, it becomes a 'bloated controller', the anti-pattern the principle warns about.

code

pseudocode · 14 lines
pseudocode
// UI adapter: no business rules, only translation
onPlaceOrderClicked(form):
    cmd    = PlaceOrder(customerId: session.user, items: form.items)
    result = placeOrderHandler.handle(cmd)   // <- GRASP controller
    render(result)

// Use-case controller: coordinates, delegates, does not compute
class PlaceOrderHandler:
    handle(cmd):
        customer = customers.byId(cmd.customerId)
        order    = customer.placeOrder(cmd.items)   // rules live in the domain
        orders.save(order)
        events.publish(OrderPlaced(order.id))
        return OrderSummary(order.id, order.total)

go deeper

for a junior

Name it: the event goes to a non-UI object, not to the button handler; the controller delegates to domain objects. One concrete example is enough.

for a middle

Give both variants — facade controller and use-case (session) controller — say when each fits, and explain the reuse/testability payoff plus the bloated-controller anti-pattern.

for a senior

Connect it to layering: framework controller = adapter, GRASP controller = application/use-case layer, rules in the domain. Discuss where transactions and authorization sit, and when the extra layer isn't worth it (simple reads).

for a principal

Frame it as a boundary policy: one entry point per use case gives a consistent seam for transactions, authz, idempotency, tracing, and API versioning; explain how it maps onto hexagonal ports, CQRS handlers, and evolvability across multiple delivery channels.

## What GRASP is **GRASP** stands for *General Responsibility Assignment Software Patterns* (Craig Larman, *Applying UML and Patterns*). It is a set of nine naming-and-reasoning principles — Information Expert, Creator, **Controller**, Low Coupling, High Cohesion, Polymorphism, Pure Fabrication, Indirection, Protected Variations — each of which answers one question of the form *"which object should have this responsibility?"* They are not code structures you download; they are decision heuristics you apply while assigning responsibilities. ## The question Controller answers > A **system event** (also called a system operation or input event) has arrived — a user pressed *Place Order*, an HTTP `POST /orders` came in, a message landed on a queue, a scheduled job fired. **Which object receives it first, on the non-UI side?** Controllers exist because the alternative is worse. Without one, the natural place to put the logic is the thing that physically caught the event: the button's click handler, the web framework's request handler, the console loop. That works exactly once — until you need a second UI, a scripted/batch path, or an automated test. ## The rule, stated Assign the responsibility for handling a system input event to a class representing **one** of: 1. **The overall system, a "root object", a device, or a major subsystem** — this variant is called a **facade controller**. Example: `OrderingSystem`, `PosSystem`, `PaymentGateway`. One object fronts a whole subsystem. 2. **A use case scenario within which the event occurs** — a **use-case controller** (Larman also calls it a *session controller*), usually named `<UseCase>Handler` or `Handle<UseCase>` or `<UseCase>Service`: `PlaceOrderHandler`, `CheckoutService`, `RegisterUserUseCase`. One object per use case (or per closely related family of use cases). Crucially, the controller is a **non-UI object**. It must not import, reference, or know about widgets, HTTP request/response types, HTML, terminal escape codes, or view models of a specific framework. It speaks in domain terms and plain data. ## What the controller actually does A well-behaved controller **coordinates and delegates**; it does not compute. A typical body: 1. Convert incoming primitive/DTO input into domain concepts (or accept an already-converted command object). 2. Load the relevant domain objects via repositories/gateways. 3. Call domain methods on those objects — the business rules live *there*, per the Information Expert principle ("give the responsibility to the class that has the data needed to fulfil it"). 4. Persist / publish results, commit the unit of work. 5. Return a plain result (a DTO, an id, a domain-level outcome) to the caller. The UI layer then decides how to *display* that result. That split is the whole point: **the controller is where the use case lives; the UI is where the pixels live.** ## Why it pays off - **Reuse across delivery mechanisms.** The same `PlaceOrderHandler` serves a web form, a mobile API, a CSV importer, and a test. - **Testability.** You can drive the use case in-process with no browser, no HTTP, no framework container. This is usually the single biggest practical win. - **A place for cross-cutting concerns.** Transactions, authorization checks, idempotency keys, audit logging, metrics — all have one obvious seam per use case. - **State across events.** A use-case controller can hold or look up the state of a multi-step scenario ("we are mid-checkout, three items scanned"), which the stateless UI cannot sensibly own. - **Protection from UI churn.** Redesigning the screen, or swapping React for something else, doesn't touch the use case. ## Costs and edge cases - **Extra indirection.** For a genuinely trivial read ("show me this record") a controller layer can be ceremony. Many teams allow simple queries to go straight from the endpoint to a query object/read model and reserve controllers for state-changing operations. - **Bloat risk.** A facade controller that fronts a large system accumulates dozens of methods and unrelated state — low cohesion. The standard remedy is to split into use-case controllers. - **Controller name collision.** "Controller" in **MVC** and "controller" in a web framework (`@RestController`, `ActionController`, ASP.NET `Controller`) are *presentation-layer adapters*. GRASP Controller sits **behind** them. A framework controller that just deserializes, calls a use-case object, and serializes the answer satisfies GRASP; one that contains the business rules does not. ## Rule of thumb If you can't run your use case from a test without starting the UI or the web framework, you don't have a GRASP controller yet.

  • Does GRASP Controller mean every system event needs its own class?
    No. It says the event must be received by a non-UI coordinator; granularity is a separate choice. A facade controller can host many related events; splitting into one class per use case is the remedy you reach for when the facade loses cohesion or accumulates unrelated state.
  • If the controller must not contain business logic, where does the logic go?
    Into the domain objects that own the data needed to decide — that is the Information Expert principle. The controller only sequences calls, handles the unit of work, and shapes the reply.
  • How do you know a class is really a controller and not just a UI handler with a different name?
    Check its dependencies. If it references request/response objects, widgets, HTML, or session cookies, it's still presentation. A controller's imports should be domain types, repositories, and plain data.

A restaurant server takes your order at the table but doesn't cook it. The server is the single point of contact for your 'order' event, translates it into instructions for the kitchen, and brings back the result. Swap the dining room for a drive-through window and the kitchen doesn't change.

saying these in an interview costs you the question

  • Saying GRASP Controller is the same thing as the 'C' in MVC or a web framework's @Controller class
  • Claiming the controller should contain the business rules because 'it handles the request'
  • Assuming every controller must be stateless — a use-case/session controller may legitimately hold scenario state
  • Believing a controller must be one-per-screen (it is per system event / use case, not per view)
  • Treating the controller as an object that talks to the UI — the dependency points the other way: UI depends on controller

context