Learn how the Bridge pattern helps Clojure mobile systems separate shared product logic from Android, iOS, and browser-specific implementation details.
Bridge pattern: A structural pattern that separates a high-level abstraction from the low-level implementation details behind it so the two can evolve independently.
The Bridge pattern matters in mobile systems because product logic and platform logic change for different reasons. Product teams change sign-in flows, session rules, or offline behavior because requirements change. Platform teams change Android, iOS, browser, or SDK integrations because operating systems, APIs, and security constraints change. If those concerns are tangled together, every platform change becomes a product rewrite and every product change becomes a platform regression risk.
In Clojure, the pattern maps naturally onto protocols, records, and data-driven orchestration. The goal is not to mimic object-oriented class diagrams for their own sake. The goal is to keep shared domain policy separate from platform-specific capability code.
Mobile teams often need one shared feature model with several implementations:
These are strong Bridge candidates because the abstraction is stable enough to name, while the implementation details vary meaningfully by platform.
It is a weak fit when there is no real abstraction yet and only one implementation exists. In that case, adding a Bridge too early just creates ceremony.
The most useful mobile Bridge examples usually involve device capabilities. Secure session storage is a good one because the app wants one product-level concept, but the host platform provides different primitives.
The abstraction here is not “Android storage” or “iOS storage.” It is “session credential storage with app-defined policy.”
1(defprotocol SecureStoreImpl
2 (read-secret [this key])
3 (write-secret [this key value])
4 (delete-secret [this key]))
5
6(defprotocol SessionStore
7 (load-session [this])
8 (save-session [this token])
9 (clear-session [this]))
10
11(defrecord SecureSessionStore [impl parse-token encode-token]
12 SessionStore
13 (load-session [_]
14 (some-> (read-secret impl :session-token)
15 parse-token))
16 (save-session [_ token]
17 (write-secret impl :session-token (encode-token token)))
18 (clear-session [_]
19 (delete-secret impl :session-token)))
This abstraction owns product semantics:
The platform implementation owns the storage mechanism itself.
1(defrecord AndroidKeystoreStore [keystore-client]
2 SecureStoreImpl
3 (read-secret [_ key]
4 (.read keystore-client (name key)))
5 (write-secret [_ key value]
6 (.write keystore-client (name key) value))
7 (delete-secret [_ key]
8 (.delete keystore-client (name key))))
9
10(defrecord IOSKeychainStore [keychain-client]
11 SecureStoreImpl
12 (read-secret [_ key]
13 (.read keychain-client (name key)))
14 (write-secret [_ key value]
15 (.write keychain-client (name key) value))
16 (delete-secret [_ key]
17 (.delete keychain-client (name key))))
The shared session rules stay stable even if one platform changes its native SDK, permission model, or encryption wrapper.
The diagram below focuses on the part teams most often blur together: shared feature policy on one side, platform capability code on the other.
Clojure does not need heavyweight inheritance to express the pattern. A protocol plus records or plain maps at the orchestration layer is usually enough.
That gives you a few practical benefits:
This is especially helpful when Clojure sits inside a mixed stack. For example:
The Bridge pattern lets those layers meet on purpose instead of through accidental coupling.
Bridge and Adapter are easy to confuse because both involve boundaries.
Use Bridge when:
Use Adapter when:
In practice, a mobile architecture often contains both. A Bridge may define SecureStoreImpl, while each concrete implementation uses Adapters around awkward host APIs or SDK wrappers.
The failure modes are predictable:
The last one is especially common. “PlatformBridge” with fifty methods is usually not a Bridge. It is a dumping ground. Better mobile design comes from several small, capability-focused boundaries.