Types of dependencies

The term "dependency" is used very often in software engineering, but depending on context, it may mean slightly different things. To avoid confusion, here are the different meanings of the term, and their explanations.

(Useful pre-reading: About these papers)

  • Compile-time (static) dependency: When module A makes use of a symbol which is defined in module B, we say that A has a compile-time dependency on B. (Or that B is a compile-time dependency of A.) This happens not only when module A contains a hard-coded invocation to module B, but also when A makes use of some definition from B, such as referring to a constant, implementing an interface, or instantiating a type defined in B.
  • Runtime (dynamic) dependency: When module A is given, at runtime, a reference to invoke module B, then we have a runtime dependency between A and B. Runtime dependencies can be further divided in two sub-categories:
    • Assembly-time (semi-dynamic) dependency: This is a runtime dependency which is realized during system assembly, and remains unchanged throughout the lifetime of the system.
    • Post-assembly-time (fully dynamic) dependency: This is a runtime dependency which may be realized or changed at any moment, by having one module programmatically pass a callback to another module.

If we are to take the Dependency Inversion Principle (DIP) (W) for granted in software architecture, (and we should,) then software architecture is not concerned with static dependencies. This is because the DIP states that concrete modules should never statically depend on other concrete modules; instead, concrete modules may statically depend only on abstractions. Thus, the DIP is advising us to build our concrete modules so that they have no knowledge of each other. Instead, they should be making outgoing invocations to interfaces, and these invocations should be wired to concrete modules implementing those interfaces. Interfaces are abstractions, so it is okay for a concrete module to have compile-time dependencies on modules defining such abstractions.

Assembly-time dependencies are what software architecture is mostly concerned with. The architecture of a software system specifies how to wire interface invocations between components. The wiring prescribed by the design is normally performed during system assembly, which is part of system deployment. Thus, the wires constitute assembly-time dependencies, and the graph of these dependencies is essentially the call graph of the system as defined by the architecture.

Post-assembly-time dependencies do not affect the topology of a design, because every post-assembly time dependency requires an existing assembly-time dependency through which the callback can be communicated. Thus, post-assembly-time dependencies constitute implementation details of the modules that supply and invoke callbacks. As such, they are of only limited interest in software architecture.

No comments:

Post a Comment