Nguyen Le PhongNguyen Le Phong

소프트웨어 아키텍처 기초15부 중 14부

Dependency Direction in Plain Language

Dependency direction is the simple team rule that says stable business ideas should not depend on volatile delivery details. This explainer uses a small release scenario to show how the rule keeps code, reviews, and team conversations calmer.

The pull request looked small at first. A product manager wanted one more rule on refunds, a developer opened a few files, and then the review started stretching across the afternoon. The rule lived partly in an API controller, partly in a database query, partly in a shared helper, and partly in a test fixture that nobody wanted to touch. By the time the team understood the change, the actual business decision felt like the easiest part.

This is where dependency direction becomes useful. It is not a fancy architecture slogan. It is a plain agreement about who is allowed to know what. The refund rule should know the language of refunds. It may know that an order can be approved, rejected, expired, or already settled. It should not need to know which HTTP framework received the request, which ORM loaded the row, which queue publishes the notification, or which payment provider SDK happens to be used this quarter.

A simple way to say the rule is this: code that changes for deep business reasons should not depend on code that changes for delivery reasons. The core idea should sit in the quieter part of the system. The adapters around it can change when the outside world changes. The API shape can move. The database library can be replaced. The vendor can rename a field. The rule should not have to move every time one of those details moves.

Imagine a small team shipping a refund feature. The product manager cares about policy: which orders are refundable, how long the window lasts, what happens when an item has already shipped, and when a human approval is required. The backend developer cares about request validation, persistence, provider calls, and events. QA cares about scenarios. Support cares about the message the user sees. If all of those concerns are mixed in the same files, every conversation becomes crowded. Nobody is sure whether they are reviewing the refund policy or the plumbing that carries it.

With dependency direction, the team can draw a calmer line. The refund policy becomes the thing other code calls. It can expose a function or use case such as requestRefund. Around it, a web handler translates an HTTP request into input. A database adapter loads the order. A payment adapter asks the provider to reverse money. A notification adapter sends the message. The outside pieces depend on the refund rule because they serve it. The refund rule does not depend on them because it should not be shaped by their temporary details.

This does not mean the core is more important than the rest of the work. Users still need routes, screens, logs, retries, and messages. It only means different kinds of code have different rates of change. Business language should be protected from noisy edges. Edges should be easy to replace because edges are where the world keeps negotiating with us: new SDKs, new auth headers, new event names, new tables, new UI flows, new infrastructure decisions.

The testing benefit is often where the idea becomes visible. If the refund rule depends directly on a real database and a real payment SDK, every test has to drag those things into the room. The team starts writing fewer tests because each one is slow, fragile, or hard to set up. If the rule depends on small interfaces or plain input data instead, the team can test the policy in minutes: refundable order, expired window, shipped item, duplicate request, manual approval. The delivery details still need tests, but they no longer block the team from understanding the rule itself.

The review benefit is quieter but just as valuable. A reviewer can ask, is this the policy we agreed on? Another reviewer can ask, is this adapter handling the provider safely? Those are different questions. When the direction is clear, the pull request tells a cleaner story. The team can discuss product behavior without getting lost in framework ceremony, and it can discuss framework behavior without accidentally changing product policy.

Dependency direction also helps when teams disagree about architecture vocabulary. One person may say Clean Architecture, another may say Ports & Adapters, another may dislike both names. For day-to-day alignment, the vocabulary matters less than the question: does the stable rule know about the volatile detail, or does the volatile detail point inward to the stable rule? If the rule imports the framework, the direction is probably backwards. If the framework calls the rule through a small boundary, the direction is easier to reason about.

There is a practical limit. A tiny script, a short-lived prototype, or a feature with no meaningful policy may not deserve a formal boundary. Over-design can make a small change feel like paperwork. The point is not to wrap every line in an abstraction. The point is to notice the parts of the codebase where the team keeps having the same confused conversation. When a business rule is important enough to survive multiple screens, vendors, jobs, or release cycles, it deserves a place where those outside details do not keep leaking in.

In plain language, dependency direction is team hygiene. It gives people a shared answer to a small but expensive question: when this changes, who should have to care? If the answer is everyone, the system will keep making small releases feel larger than they are. If the answer is clear, the team can change the edge without disturbing the core, and change the core without pretending it is only an edge. That clarity rarely appears in one dramatic refactor. It grows through many small choices in code review, naming, tests, and the patience to ask whether a dependency is pointing toward the thing that should stay steady.

If your team has a feature that always feels harder to change than its product rule suggests, it may be worth opening the files and tracing the arrows together. Not to shame the old design, but to see which details are pulling the conversation away from the decision the team actually needs to make.

이 글 어떠셨나요?