Introduction
This post discusses various approaches for creating a software interface, however, before considering how software modules interface with each other, it is worth considering the nature of discrete software elements in general.
As an axiomatic definition, we can state that:
Every software module is, at once, both an independent functional unit and part of a larger system.
In other words, every software component presents capabilities outwardly, while internally it manages the logic, data structures, and error handling which are needed to present its external capabilities.
Letās drill down into what that means in practice.
Software Context and Integrity
Regardless of size or complexity, every individual software component exposes some public behaviorāthis is the visible set of functions or features that external consumers (end users, other subsystems, or automated scripts) can perceive and interact with.
This outward behavior might take the form of a graphical interface, an API endpoint, or a library function call, and usually determines how the software is identified: āThis is a CRM,ā āThis is a payment processing module,ā āThis is a messaging service,ā and so on.
Software Modules as as Independent Functional Unit
Beneath a moduleās external interface lies the implementation itself: the internal code, data structures, and logical flows that carry out the real work. While the public behavior tells the outside world āwhatā the software does, the internal logic determines how it truly operates. Both dimensionsāexternal interface and internal logicādefine what a software module is, and how it can reliably fulfill its role.
In terms of internal logic, each software component maintains its own identity by being self-defined in terms of domain rules, data models, and error-handling. This self-definition effectively places each module at the center of its own world, focusing on the part of the system for which it is responsible.
For instance, a Billing module internally enforces payment rules, transaction validations, and ledger updates, whereas a Reporting module structures data and generates outputs in a way that suits its analytics focus.
External Definition + Internal Structure
Collectively, these two aspectsāthe publicly visible behavior and the self-defined internal structureāgive each module a clear position within the broader software ecosystem.
A well-defined software module can present a reliable, understandable interface outwardly while still preserving the freedom to modify its internal workings, as long as the public contract remains intact.
The Interface Challenge

Modern software systems rarely exist as a single, standalone module. Instead, multiple software modules communicate with each other to achieve the desired end result.
Since the fidelity of a software module depends on the extent to which it clearly defines and controls its own internal state and domain specific logic, every time an interface is required between two or more software modules, there will inevitably be a misalignment between the way one module āsees thingsā (internally) and the point-of-view of the other module(s).
Subsequently we arrive at a central challenge in software systems design:
How do these individually self-defined modules communicate and collaborate without stepping on each otherās conceptual toes?Ā
Or, in other words:
Every software system must reconcile two seemingly contradictory paradigms:
- Local Domain Logic: Each subsystem or module interprets requests, data, and events through its unique lens. It has its own definitions of āvalid data,ā āerror conditions,ā and ābusiness rules.ā
- Broader System Perspective: In order to create synergy between the specific software modules and deliver coherent features to end users or other consumers, the overall application must ensure modules work together smoothly, avoiding conflicting assumptions or duplicated responsibilities.
This challenge must be addressed in software interfaces which define:
- How independently defined modules agree to communicate regarding data exchange, error signaling, etc.
- And how each serviceās public behavior fits into the bigger picture.
If we consider each software module or subsystem as its own āsystemā, then we can refer back to the way general systems theory describes how different systems can interact, in order to understand from a high-level conceptual perspective, the different approaches we can take in designing software interfaces.
Reconciling Different System Perspectives
As explained in previous posts (Defining Truth in Systems Thinking,Ā Systems Analysis of a Contract), when different systems interact, and each system has an internal representation of the truth which is different (or even opposite) to the other system, there are two ways in which it is possible to combine these viewpoints.
- Implicit: The facts, from the perspective of each system, are presented as are, without any attempt to translate the different systemsā paradigms into each otherās terms.
In this case, the āentire truthā is implicit, or becomes self-evident, simply by the acceptance of both ātruthsā simultaneously.
- Explicit: We develop a higher-level understanding (or conceptual framework) that both systems can map to.
The higher-level understanding actively reconciles the different viewpoints into a single, more general structure.
In other words, general systems theory posits that each system (or subsystem) has its own coherent worldview, and no single systemās vantage is either definitive or āwrongā. However, to collaborate, these differing systems must find a way to coexist without overriding each otherās internal coherence.
- This coexistence can be accomplished passively, just by allowing each system to exist and communicate on its own terms.
- Or it can be accomplished actively, by ascending to a higher level of abstraction which can accommodate the localised viewpoint of each system.
Two Approaches to Interfacing Software

Just as we find two approaches to allowing different systems to coexist, so too, in software terms, we can identify two basic approaches to creating an interface.
This means that in order to allow software modules, or subsystems, or systems to communicate effectively, we can take one of two paths:
- We can allow each moduleās distinct worldview to remain intact (accepting that they each have their own ātruthā about data formats, domain concepts, etc.), then define a bridging mechanism (an adapter, gateway, or translation layer).
This parallels the implicit approach in systems theory: each viewpoint stands on its own, but we let them exchange data with minimal modification to the modules themselves.
- Or we can ascend to a vantage that is broader than any single moduleās local viewpoint. In practice, this means that we define a shared model, domain-driven core, or orchestrator that each module partially adopts.
This is the explicit approach, echoing the āhigher-level understandingā in systems theory: we unify around a conceptual framework that provides a broad, abstract perspective that can accommodate the data or logic peculiar to each software component.
Below, weāll look in more detail at these two approaches and how they shape the design of software interfaces.
Implicit (Parallel) Interface Approach
This is the ākeep each paradigm intactā interface strategy. Here, the emphasis is on minimizing disruption to a subsystemās internal logic and relying on adapters or translation layers to bridge the differences.
In simpler terms, each module maintains its own conventions, data formats, and domain-specific ātruth,ā while a dedicated layer handles conversions between them. This strategy aligns closely with the implicit concept in systems theory: we allow each subsystemās vantage to exist unaltered, accepting multiple ātruthsā in parallel.
OverviewUnder the implicit model, each subsystem preserves its local definitions, rules, and data formats. Instead of forcing a common language, specialized translation layers or adapters convert data and function calls between modules. The modules effectively talk āas themselves,ā with the adapter reconciling differences. Why It Makes SenseAdopting this model places minimal burden on the subsystemās core code, focusing changes on the interface boundary. ExampleSuppose Billing has an API method called chargeAmount(), and Reporting expects processPayment() calls. Pros and ConsPros
Cons
When to UseThis strategy is good for small-scale integrations, particularly if one or two modules must be stitched together quickly, and is often the go-to for legacy compatibility (where rewriting an older subsystem is impractical). |
Explicit (Elevated) Interface Approach
This is the āascend to a higher abstractionā interface strategy. Here, we create a shared conceptual framework (like a canonical data model or domain-driven core) so that modules align with a single vantage for data and operations, rather than directly translating among themselves.
In practice, each subsystem still retains its internal logic, but it adapts that logic to a larger vantageāthe overarching conceptual model. This parallels the explicit approach in systems theory, where we synthesize a unified perspective that reconciles all local definitions at a higher level of abstraction.
OverviewBy introducing a central reference, each subsystem āspeaksā to this abstraction rather than one anotherās native formats. This can reduce confusion and friction across the organization, as everyone references the same conceptual definitions. Why It Makes SenseA single unified vantage fosters consistency across large, complex systems. Rather than building a patchwork of adapters for each pair of modules, you maintain a single shared language. This approach typically works best if your software ecosystem is extensive, or if you aim to evolve the system while preserving coherent data definitions. ExampleConsider an Enterprise Service Bus (ESB) with a canonical āOrderā object. Pros and ConsPros
Cons
When to UseThis method suits large, intricate environments such as microservices architectures. It also pays off when you expect your system to evolve substantially, and you need to keep conceptual definitions aligned over time. |
Mapping Classical Interfacing Patterns to System Theory
Below is a table that connects common interfacing patterns from software architecture to the theoretical approaches described earlier.
The examples illustrate how each pattern either keeps each systemās local paradigm intact (implicit/parallel) or employs a more general vantage (explicit/elevated).
| Interface Pattern | Systems-Theory Approach | Example | Notes |
| Direct āLocal Termsā Exchange | Keep Each Subsystemās Paradigm Intact (Implicit) | Scripting two microservices to talk via domain-laden JSON | Each side sees its own viewpoint as primary; the interface simply passes fields. A translator or adapter might do minimal rewriting. |
| Gateway or Adapter āon the Flyā | Keep Each Subsystemās Paradigm Intact (Implicit) | An āAdapterā service between Domain A and Domain B | Each sideās rules remain distinct; the adapter transforms requests and responses. Commonly found in āAPI Gateway + internal microservicesā setups. |
| Shared Data Model / āUnifying Abstractionā | Ascend to a Higher Abstraction (Explicit) | Enterprise bus with a ācanonical orderā object for all modules | Local illusions (subsystemsā data) are re-expressed in the canonical model, then re-labeled for each receiving system. |
| Domain-Driven Core | Ascend to a Higher Abstraction (Explicit) | In DDD (Domain-Driven Design), define a āUbiquitous Languageā | Each bounded context has local definitions mapped into the shared domain concepts. A synergy vantage emerges from the ubiquitous language. |
| Mediator or Orchestrator | Ascend to a Higher Abstraction (Explicit) | A workflow engine that manages multiple subsystems. | Logic is centralized; the orchestrator holds the ālarger vantage,ā guiding how each subsystemās data and functions form a bigger workflow. |
Choosing Between Approaches
Recognizing each software module as an independent system clarifies why bridging them effectively must follow one of two major paths:
- Parallel (Implicit) ā Every module retains its worldview, and a translator or adapter resolves differences.
- Elevated (Explicit) ā A more general model stands above the modules, with each conforming to that model at the boundary.
Neither approach is inherently ābetterā; instead, the optimal choice depends on factors like how many modules you need to integrate, how often they change, and whether you desire a coherent, enterprise-wide view of data. Smaller or more static clusters of modules might benefit from parallel bridging, while larger or evolving ecosystems may favor the structure of a higher vantage.
Ultimately, designing interfaces involves translating local definitions (the parallel approach) or unifying them under a shared abstraction (the elevated approach). The goal isnāt about choosing one subsystem as having the āright perspective,ā but rather the goal is to reconcile multiple valid perspectives so the system as a whole remains coherent, robust, and maintainable.
Ā
