Sitemap
Anuanu

Anuanu

Understanding Uncle Bob’s Component Principles

3 min readJun 15, 2026

--

Press enter or click to view image in full size

Many developers learn Uncle Bob’s Component Principles from theory but struggle to understand where they apply in real projects.

Let’s take a simple Android BLE Scanner application as an example.

Imagine our project has two modules:

core-ble-scanner
feature-ble-scanner

core-ble-scanner

Contains:

  • BLE scanning logic
  • Scan manager
  • Bluetooth APIs
  • Device discovery logic

feature-ble-scanner

Contains:

  • Scan screen
  • RecyclerView
  • Compose UI
  • Device list display
  • User interactions

Architecture:

feature-ble-scanner
↓
core-ble-scanner

The UI depends on the scanner logic.

1. Reuse/Release Equivalence Principle (REP)

Idea

If something can be reused, it should be packaged as a reusable component.

Bad Example

BLE scanning code is written directly inside the UI module.

feature-ble-scanner
├── ScanScreen
├── DeviceList
└── BluetoothScanner

Now every feature that needs BLE scanning must copy the code.

Better Example

Move scanner logic to a separate component.

core-ble-scanner
├── BleScanner
├── ScanManager
└── BluetoothRepository

Now another feature can reuse it.

feature-ble-scanner
feature-device-pairing
feature-device-diagnostics

All use the same scanner component.

Lesson

Reusable code should live in its own reusable component.

2. Common Closure Principle (CCP)

Idea

Things that change together should stay together.

Example

Suppose Android introduces a new BLE scanning API.

What changes?

BleScanner
ScanManager
BluetoothRepository

All these classes belong to scanning.

They change for the same reason.

Therefore they belong together.

core-ble-scanner
├── BleScanner
├── ScanManager
└── BluetoothRepository

The UI doesn’t change.

So it should stay separate.

Lesson

Keep BLE logic together because it changes together.

3. Common Reuse Principle (CRP)

Idea

Don’t force modules to depend on things they don’t need.

Bad Example

core-ble-scanner
├── BleScanner
├── ScanManager
├── ScanScreen
├── DeviceAdapter
└── DeviceCard

Now another feature only needs scanning.

But it must also depend on:

  • Compose UI
  • RecyclerView
  • UI resources

which it doesn’t need.

Better Example

core-ble-scanner
├── BleScanner
└── ScanManager
feature-ble-scanner
├── ScanScreen
├── DeviceCard
└── DeviceList

Each module depends only on what it needs.

Lesson

Separate reusable logic from UI.

4. Acyclic Dependencies Principle (ADP)

Idea

Dependencies should not form circles.

Bad Example

feature-ble-scanner
↓
core-ble-scanner
↓
feature-ble-scanner

How can this happen?

Get Anuanu’s stories in your inbox

Join Medium for free to get updates from this writer.

Suppose:

core-ble-scanner

calls

ScanScreen.showError()

Now the core module depends on the feature module.

And the feature module already depends on core.

We created a cycle.

Better Example

feature-ble-scanner
↓
core-ble-scanner

Only one direction.

Core never knows about UI.

Lesson

Core modules should not depend on feature modules.

5. Stable Dependencies Principle (SDP)

Idea

Depend on stable things.

Example

UI changes frequently.

Maybe today:

RecyclerView

Tomorrow:

Jetpack Compose

Next month:

Different design

UI is unstable.

BLE scanning logic changes less often.

BleScanner
ScanManager
BluetoothRepository

are relatively stable.

Therefore:

feature-ble-scanner
↓
core-ble-scanner

is correct.

Not:

core-ble-scanner
↓
feature-ble-scanner

Lesson

Unstable UI should depend on stable business logic.

6. Stable Abstractions Principle (SAP)

Idea

Stable modules should expose abstractions.

Example

Instead of exposing implementation:

class AndroidBleScanner

Expose an interface.

interface BleScanner {
fun startScan()
fun stopScan()
}

Implementation:

class AndroidBleScanner : BleScanner

Now future implementations can be added.

FakeBleScanner
MockBleScanner
AndroidBleScanner

without changing consumers.

Lesson

Stable components should expose interfaces, not concrete implementations.

Final Architecture

core-ble-scanner
│
├── BleScanner
├── AndroidBleScanner
├── ScanManager
├── BluetoothRepository
└── Models

feature-ble-scanner
│
├── ScanScreen
├── DeviceList
├── DeviceCard
└── ViewModel

Dependency direction:

feature-ble-scanner
↓
core-ble-scanner

This architecture follows all six of Uncle Bob’s Component Principles:

  • Reusable scanner logic is isolated
  • BLE-related changes stay together
  • UI is not forced on other features
  • No circular dependencies
  • UI depends on stable logic
  • Stable logic exposes abstractions

And that’s exactly why many modern Android projects separate core modules from feature modules. This isn’t just about keeping code organized — it makes the application easier to maintain, test, and extend as new BLE features are added.

--

--