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.
@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 + "}";
}// 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
- AR-01Business Logic in the ControllerL4MediumComing soon
- AR-03Domain Tied to the FrameworkL4HardComing soon
- AR-04Dependencies Pointing the Wrong WayL4HardComing soon
- AR-05One Model for Reads and WritesL4HardComing soon
- AR-06A Balance With No HistoryL4HardComing soon
- AR-08Events Lost After CommitL4HardComing soon