Components, Connectors, and Configurations
The core building blocks of systems: components, the connectors between them, and the configurations that shape runtime behavior
What Are Components, Connectors, and Configurations?
In software architecture, system thinking is the practice of viewing a system as a holistic collection of interacting parts. The most fundamental of these parts are components, the connectors that link them, and the configurations that arrange them. Understanding these three elements is the first step toward designing robust, scalable, and maintainable systems.
A system's architecture is defined by its components, how they are connected, and the rules that govern their interaction. This structure dictates the system's capabilities, its quality attributes (like performance and reliability), and how it can evolve over time.
TL;DR
Components encapsulate responsibilities; connectors carry interactions (sync or async); configurations arrange both into a runnable topology. Pick one primary connector per interaction, design for timeouts, retries, and idempotency, and avoid shared databases as an integration mechanism.
Learning objectives
- You will be able to distinguish components, connectors, and configurations and explain how they interact.
- You will be able to choose an appropriate connector (REST, gRPC, queue, stream) for a given scenario.
- You will be able to sketch a configuration that supports independent deployability and observability.
- You will be able to identify anti-patterns such as shared databases and chatty synchronous calls.
Motivating scenario
Your team is decomposing a growing monolith into a few services: ProductService
, OrderService
, and a checkout web app. Performance is degraded by chatty HTTP calls and cross-service DB access. By clarifying component boundaries, choosing a single primary connector per interaction, and drawing a clear configuration, you reduce coupling, improve resilience, and make independent deployments safe.
Core Concepts
Components
A component is a deployable unit of computation or a data store—such as a service, database, or message broker. It is modular, independently deployable, and replaceable, encapsulating a set of related functions or data. Components have well-defined interfaces that hide their internal implementation details.
- Examples: A microservice, a database, a message queue, a client-side web application.
Connectors
A connector is a mechanism that mediates communication, coordination, or cooperation among components. Connectors are the "plumbing" of an architecture, enabling data and control flow. They are distinct from the components themselves and can range from simple procedure calls to complex, network-based protocols.
- Examples: A REST API call, a gRPC connection, a message bus, an event stream, or a shared memory buffer.
Configurations
A configuration is the structural arrangement of components and connectors. It describes the topology of the system—how the parts are wired together to form a whole. The configuration defines the system's runtime structure and dictates how components interact to fulfill the system's purpose.
- Examples: A client-server topology, a pipeline of data-processing services, a star-shaped topology with a central message broker.
Practical Examples and Real-World Scenarios
Let's consider a simple e-commerce system.
- Components:
ProductService
: Manages product information.OrderService
: Handles order creation and processing.PostgreSQL DB
: A relational database for storing order data.Kafka Topic
: An event stream for publishing order events.
- Connectors:
gRPC
: Used for synchronous, internal communication betweenOrderService
andProductService
.Postgres Driver
: The connector used byOrderService
to interact with its database.Kafka Producer/Consumer
: The mechanism for writing to and reading from theOrder-Events
topic.
- Configuration:
The
OrderService
receives a request, calls theProductService
via gRPC to validate product details, persists the order to itsPostgreSQL DB
, and publishes anOrderCreated
event to aKafka Topic
.
Practical Configuration Example
A minimal docker-compose.yml
to stand up the components and connectors from the example above.
version: "3.9"
services:
product:
image: ghcr.io/example/product-service:latest
environment:
- GRPC_PORT=50051
ports:
- "50051:50051"
order:
image: ghcr.io/example/order-service:latest
depends_on:
- product
- postgres
- kafka
environment:
- PRODUCT_GRPC_ADDR=product:50051
- DATABASE_URL=postgres://postgres:postgres@postgres:5432/orders?sslmode=disable
- KAFKA_BROKERS=kafka:9092
ports:
- "8080:8080"
postgres:
image: postgres:15
environment:
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=orders
ports:
- "5432:5432"
zookeeper:
image: confluentinc/cp-zookeeper:7.6.1
environment:
ZOOKEEPER_CLIENT_PORT: 2181
kafka:
image: confluentinc/cp-kafka:7.6.1
depends_on:
- zookeeper
environment:
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
ports:
- "9092:9092"
Decision Model: Choosing the Right Connector
The choice of connector is a critical architectural decision that impacts performance, reliability, and coupling. Use this mental model to guide your selection.
Decision Matrix: Connector Trade-offs
Option | Coupling | Performance | Complexity | Typical use |
---|---|---|---|---|
REST API | Low | Medium | Low | External-facing APIs; simple integrations |
gRPC | Medium | High | Medium | Internal service-to-service; low latency |
Message Queue | Very Low | High | Medium | Asynchronous tasks; load leveling |
Event Stream | Very Low | Very High | High | Event sourcing; stream processing |
Shared Database (anti-pattern) | High | High | Low | Legacy monoliths; avoid for integration |
Implementation Notes / Patterns / Pitfalls
- Define clear, stable interfaces for your components.
- Choose connectors that match your coupling and performance needs.
- Isolate components to allow for independent deployment and scaling.
- Use asynchronous connectors to improve resilience and elasticity.
- Do not let components bypass connectors and interact directly (e.g., direct DB access across services).
- Avoid creating a "distributed monolith" by using synchronous, blocking calls everywhere (see ../../anti-patterns-and-pitfalls/distributed-monolith).
- Do not share databases between components (services) as it creates high coupling (see ../../anti-patterns-and-pitfalls/shared-database-across-services).
- Do not build custom connector logic when a standard, off-the-shelf solution exists.
Hands‑on exercise: Map and improve a real flow
- Pick one user flow (e.g., create order). Draw its current configuration using a simple Mermaid diagram with components and connectors.
- Identify the primary connector per interaction and remove unnecessary chatty calls; consider replacing sync hops with an event or queue where appropriate.
- Define timeouts, retries with jitter, and idempotency keys at each connector.
- Add basic SLOs (success rate, p95 latency) and instrument logs/metrics/traces at boundaries.
Edge Cases and Failure Modes
- Timeouts and retries: set per-connector deadlines; prefer exponential backoff with jitter.
- Partial failures: design idempotent handlers and compensating actions; prefer async where feasible.
- Duplicate deliveries: ensure consumers are idempotent; use deduplication keys.
- Thundering herds: apply rate limits and circuit breakers at connector boundaries.
- Multi-tenant isolation: avoid cross-tenant data leakage; segregate topics/queues and database schemas.
Operational Considerations
- SLOs/SLIs: Each component should have defined Service Level Objectives (SLOs). For example, the
ProductService
might have an SLO for 99.9% availability and a 50ms 99th percentile latency. Connector behavior (e.g., retry policies, timeouts) directly impacts these metrics. - Rollouts: Components should be independently deployable. A change to the
OrderService
should not require a redeployment of theProductService
. Use techniques like blue-green or canary deployments to roll out new component versions safely. - Quotas/Limits: Implement rate limiting and quotas on component interfaces to prevent cascading failures. A connector like an API Gateway is an ideal place to enforce these policies.
Security, Privacy, and Compliance
- Authentication & Authorization: Connectors must enforce security. For example, an API Gateway can validate JWTs for external requests, while a service mesh can enforce mTLS for internal gRPC calls.
- Data Classification: Components handle data of varying sensitivity. Ensure that connectors prevent leakage. For instance, an
OrderService
should not expose PII through an insecure connector. - Secrets Management: Components often need secrets (API keys, credentials) to use connectors. These must be managed securely via a vault, not hardcoded in configurations.
Observability
- Logs: Components should produce structured logs. Connectors should add correlation IDs to trace requests as they flow through the system.
- Metrics: Each component should expose key metrics (e.g., request rate, error rate, duration - RED metrics). Connectors can also provide metrics, such as queue length for a message broker.
- Traces: Use distributed tracing to visualize the entire lifecycle of a request across multiple components and connectors. This is invaluable for debugging performance issues.
Testing
- Contract tests: verify component interfaces and connector schemas (e.g., CDC/AsyncAPI for events).
- Resilience tests: inject timeouts, retries, and broker outages; assert graceful degradation.
- Performance tests: measure latency budgets across connectors; include p99s and saturation.
- End-to-end tests: cover critical flows across components using synthetic data and correlation IDs.
When to Use / When Not to Use
When to formally define Components, Connectors, and Configurations:
- When designing a new system from scratch.
- When decomposing a monolith into microservices.
- When your system is becoming complex and you need to manage dependencies.
- When you need to communicate the architecture to new team members.
When this level of formalism might be overkill:
- For very small, single-purpose applications or scripts.
- In the early stages of a prototype where speed is more important than structure.
Design Review Checklist
Component & Connector Design Checklist
- Is the component’s responsibility well-defined and cohesive?
- Is the component’s interface clear and hiding implementation details?
- Does the chosen connector match the required performance and reliability?
- Is the communication pattern (sync/async) appropriate for the use case?
- Can the component be deployed and scaled independently?
- Are security controls (authn/authz) enforced by the connector?
- Is there a clear strategy for versioning the component’s interface?
- Are observability hooks (logs, metrics, traces) built into the component and connector?
- Have you avoided shared mutable state between components?
Related Topics
Signals & anti‑signals
When formal components/connectors help vs. add overhead
Next steps
- Calibrate connector choices with API & Interface Design
- Review reliability levers: Timeouts, Retries, Backoff, Jitter
- Plan observability with Observability & Operations
Self‑check
- What distinguishes a component from its connectors?
- When would you choose an event stream over a message queue?
- Which connector policies most affect your SLOs (and how)?
One takeaway: Favor one primary connector per interaction and design it with explicit timeouts, retries, and idempotency.