Skip to main content

Separation of Concerns

Split a program along its concerns, and let no concern know how another one does its job.

Encapsulate What Varies tells you to wall off the part of a class that changes from the part that doesn't. Separation of Concerns is the older, blunter ancestor of that idea: split any program, not just a single class, into pieces where each piece answers exactly one question, and none of them has to understand another's job to do its own.

Why it exists

Picture an OrderImportJob that reads a CSV off disk, parses each row into an Order, checks that the totals add up, and writes the survivors to the database, all inside one run() method. It works, right up until the file format changes, or validation needs a new rule, or storage swaps from a database to a queue. Every one of those changes touches the same method, because the method never separated "where the bytes come from" from "what a valid row looks like" from "where a valid row goes." A bug in date parsing sends you into a method that also happens to open sockets and write SQL, and you have to read all of it to trust that your fix to one concern didn't disturb another.

Four questions, four owners

Split each question into its own object and each one gets a name that describes exactly what it does and nothing else.

OrderImportJob+ run(path)OrderFileReader+ read(path)OrderRowParser+ parse(rows)OrderValidator+ validate(order)OrderRepository+ save(order)
dependency
Four questions, four owners. The job itself just calls them in order.

None of the four new classes got smaller in total - the line count barely moved. What changed is that a bug in date parsing now points you at one file with one job, instead of a grep through a method that also happens to open sockets and write SQL.

Try it yourself: OrderImportJob above still decides when to run and in what order to call its four collaborators. Is scheduling ("run this nightly at 2am") a fifth concern that belongs somewhere else too, or is orchestration exactly what OrderImportJob itself should own? Write down your reasoning before moving on.

The cost of overapplying it

Splitting can go past the point of return. Four single-line classes, each holding one field and delegating to it, can be strictly harder to trace than the one cohesive method they replaced, if the "concerns" being separated were never actually independent questions - just busywork slicing of code that always changes together for the same reason. The test for a real concern is: does this piece have its own reason to change, separate from the others? If splitting a method in two means both halves still change in lockstep every time, you split along the wrong seam.

How it relates to its neighbours

The Single Responsibility Principle, covered later under SOLID, is Separation of Concerns applied to exactly one class at a time - "a class should have one reason to change" is this same idea, scoped down. Coupling and Cohesion is the measurement this split produces: each of the four new classes above is more cohesive on its own than OrderImportJob was holding all four jobs, and OrderImportJob is now loosely coupled to each of them, holding only a reference and calling one method.

Where you'll see it in the pattern catalog

Command separates the concern of "what should happen" from "when and how it gets invoked" - a menu item doesn't need to know how undo works to trigger it. In this site's additional patterns, Repository is Separation of Concerns applied specifically to persistence: business logic stops needing to know how data gets stored, the same move OrderRepository makes above.

Check yourself

Question 1 of 3

An OrderImportJob reads a file, parses it, validates the rows, and saves them to the database, all in one run() method. Which principle explains what to do about it, and which later SOLID principle is the class-level version of the same idea?