🦉
Programming Notes
  • My Programming Notes
  • CKA Exam Preparation
    • Certified Kubernetes Administrator
    • Setup Minikube
    • Network Design Principles
    • Role-Based Access Control (RBAC)
    • Namespace
    • Resource Quota
    • Pod
    • Deployment
    • Deployment: Rollout
    • ConfigMap
    • Service
    • Service: kubectl expose
    • Pod: Resources Management
    • Pod & Container: Quality of Service Class
    • Pod & Container: Probes
    • Limit Range
    • Scaling: Manual
    • Scaling: Horizontal Pod Autoscaler
    • Persistent Volume & Claim
    • Secret
    • Ingress: Routing
    • Ingress: TLS
    • Ingress: Rate Limit
    • Ingress: Basic Auth
    • Ingress: CRD (Custom Resource Definition)
    • Job
    • CronJob
    • Mutli-Node Cluster
  • Golang
    • Generics
    • Context
    • Goroutines and Channels in Go
    • Goroutine: Concurrency vs Parallelism
    • Goroutine: Performance & Tradeoffs
    • JSON: omitzero
  • Rust
    • Arrays & Slices
    • Closures
    • Generics & Traits
    • Iterators
    • Run Code Simultaneously
    • String vs &str
    • Tests
    • Rustlings Exercises
      • Variables
      • Functions
      • If
      • Primitive Types
      • Vectors
      • Move Semantics
      • Structs
      • Enums and Matching Pattern
      • Strings
      • Modules
      • Hashmaps
      • Options
      • Error Handling
      • Generics
      • Traits
      • Lifetimes
      • Tests
      • Iterators
      • Smart Pointers
      • Threads
      • Macros
      • Quiz 1
      • Quiz 2
      • Quiz 3
  • Software Engineering
    • CAP Theorem
    • Circuit Breaker
    • Decoupling
    • GraphQL: Query Caching
    • HMAC Signature
    • Idempotency
    • Monolith VS Microservice
    • OWASP Top 10 2021
    • PCI DSS
    • PostgreSQL: Partitioning
    • PostgreSQL: Replication
    • Protobuf & gRPC
    • Redis: Streams
    • Resource Scaling
    • Signed URL
    • SOLID
    • Stack VS Heap
    • Stateful VS Stateless
  • Site Reliability Engineering
    • Chaos Engineering
    • Distributed Tracing
    • Kubernetes (k8s)
    • SLA, SLO, and SLI Metrics
    • Site Reliability Engineer
  • Others
    • FFMPEG Cheat sheet
Powered by GitBook
On this page
  • Benefits
  • Decoupling Techniques
  • Real-World Examples
  • When Decoupling May Be Overkill
  • Golang Example
  1. Software Engineering

Decoupling

Decoupling in software engineering refers to the process of reducing dependencies between components, systems, or layers in a software application. The goal is to ensure that changes in one part of the system have minimal or no impact on other parts, promoting modularity, scalability, and maintainability.

  • Low Coupling: Components interact with each other minimally and in well-defined ways. Dependencies between components are reduced or abstracted.

  • High Cohesion: Each component focuses on a single responsibility or closely related tasks.

  • Abstractions: Interfaces, design patterns, or APIs are often used to separate concerns and decouple components.

  • Communication through Contracts: Components interact using well-defined protocols or contracts, such as interfaces or messages.

Benefits

  • Ease of Maintenance: Changes in one module are less likely to affect others.

  • Reusability: Decoupled components can be reused in other projects or contexts.

  • Testability: Isolated components are easier to test independently.

  • Scalability: Decoupled systems can scale by replacing or upgrading individual components without disrupting others.

  • Flexibility: Easier to swap out or replace one component with another.

Decoupling Techniques

  • Abstraction:

    • Use interfaces to define behaviors.

    • Example: Decouple business logic from data access logic by defining repository interfaces.

  • Event-Driven Architecture:

    • Components communicate through events instead of direct calls.

    • Example: Publish/subscribe systems.

  • Dependency Injection (DI):

    • Inject dependencies into components instead of instantiating them internally.

    • Example: Pass the database dependency into the service constructor.

  • Message Queues:

    • Use messaging systems (e.g., RabbitMQ, Kafka) for asynchronous communication between decoupled components.

Real-World Examples

  • Microservices: Each microservice is a self-contained unit with its own database and logic. Services communicate via APIs or messaging systems, not direct calls.

  • Frontend and Backend: Decoupled via RESTful APIs or GraphQL, allowing independent development and scaling.

  • Plugins and Extensions: Decoupled systems allow new features to be added without altering the core application.

When Decoupling May Be Overkill

  • Small Applications: Decoupling may add unnecessary complexity.

  • Performance Concerns: Additional abstractions or message passing can add latency.

Golang Example

Before Decoupling (Tightly Coupled System)

type MySQLDatabase struct{}

func (db MySQLDatabase) Connect() {
    fmt.Println("Connecting to MySQL...")
}

type UserService struct {
    Database MySQLDatabase
}

func (s UserService) GetUser(userID int) {
    s.Database.Connect()
    fmt.Printf("Fetching user with ID: %d\n", userID)
}

func main() {
    service := UserService{Database: MySQLDatabase{}}
    service.GetUser(1)
}

Problem: The UserService is tightly coupled with MySQLDatabase. If we want to switch to another database (e.g., PostgreSQL), we must modify the UserService code. After Decoupling (Using Abstraction)

type Database interface {
    Connect()
}

type MySQLDatabase struct{}

func (db MySQLDatabase) Connect() {
    fmt.Println("Connecting to MySQL...")
}

type PostgreSQLDatabase struct{}

func (db PostgreSQLDatabase) Connect() {
    fmt.Println("Connecting to PostgreSQL...")
}

type UserService struct {
    Database Database
}

func (s UserService) GetUser(userID int) {
    s.Database.Connect()
    fmt.Printf("Fetching user with ID: %d\n", userID)
}

func main() {
    // Switch between different database implementations without changing UserService
    mysql := MySQLDatabase{}
    postgres := PostgreSQLDatabase{}

    service := UserService{Database: mysql}
    service.GetUser(1)

    service.Database = postgres
    service.GetUser(2)
}

Solution: The UserService is now decoupled from specific database implementations. It relies on the Database interface, making it easy to replace or add new databases.

PreviousCircuit BreakerNextGraphQL: Query Caching

Last updated 4 months ago