← All categories

Architecture

Where business rules live, and which way dependencies point.

Architecture problems are bigger than one class. Business rules end up inside HTTP controllers, domain objects depend on a framework, or one data model is stretched to serve both writes and heavy reports.

These capstones ask you to separate layers and turn dependencies around, so the core rules can run and be tested without a web server, a particular database or a payment provider.

You'll recognise it when

  • Controllers validate, calculate, query and format in one method.
  • Business classes import framework, SDK or SQL types.
  • The same rules can't be run from a batch job or a test.
  • Reports add fields to the model that writes must keep in sync.

Example

A gym membership API renews memberships.

Before
@PostMapping("/renew")
String renew(@RequestBody Map<String, String> body) {
    var row = jdbc.queryForMap("SELECT * FROM members WHERE id = ?", body.get("id"));
    LocalDate expiry = ((Date) row.get("expiry")).toLocalDate();
    long fee = expiry.isBefore(LocalDate.now()) ? 1500 : 1200;   // late fee rule
    jdbc.update("UPDATE members SET expiry = ? WHERE id = ?", expiry.plusMonths(1), body.get("id"));
    return "{\"charged\":" + fee + "}";
}
After
// Core: plain Java, no web or SQL types
class RenewMembership {
    private final Members members;
    RenewMembership(Members members) { this.members = members; }

    long renew(String id, LocalDate today) {
        Member m = members.byId(id);
        long fee = m.renew(today);        // the rule lives on Member
        members.save(m);
        return fee;
    }
}

// Edge: the controller only translates HTTP to and from the core
@PostMapping("/renew")
RenewResponse renew(@RequestBody RenewRequest req) {
    return new RenewResponse(renewMembership.renew(req.id(), LocalDate.now()));
}

Why it's better: The renewal rule can now be tested in milliseconds, run from a nightly job, and kept unchanged when the database or web framework changes.

Ideas you'll practise

Layered architectureHexagonal (ports and adapters)Clean ArchitectureCQRSEvent SourcingTransactional Outbox

Problems 6