Facade: pattern fundamentals in mobile architecture

Author: IT Sectr Published: 2026-02-18 Reading time: 9 min

Facade is a structural design pattern that provides a simplified interface to a complex subsystem of classes. In mobile development, Facade is most often implemented as a Service Layer or UseCase, hiding interactions with the network, database, and analytics. According to Martin Fowler (Patterns of Enterprise Application Architecture, 2003), Facade is one of the key patterns for organizing a service layer.

Key Takeaways

  • Facade — a structural pattern that provides a simple interface to a complex system of classes, libraries, or frameworks
  • Service Layer — a Facade implementation in mobile architecture that hides API, cache, and analytics from the UI
  • Facade does not hide the subsystem — the client can access it directly when needed
  • UseCase in Clean Architecture — a variant of Facade that orchestrates a single business scenario
  • Facade vs Adapter: Facade simplifies the interface, Adapter converts one interface into another

What is the Facade pattern?

Facade is a structural pattern that provides a unified interface to a group of subsystem interfaces. It defines a high-level interface that simplifies the use of the subsystem. Facade does not add new functionality — it orchestrates existing components, hiding the complexity of their interaction from the client.

Kotlin
// Complex subsystem
class AuthApi {
    suspend fun login(email: String, pass: String): TokenResponse
}

class UserDao {
    suspend fun saveUser(user: UserEntity)
    suspend fun getUser(id: Long): UserEntity?
}

class AnalyticsTracker {
    fun track(event: String, params: Map)
}

// Facade — a simple interface for the UI
class AuthService(
    private val api: AuthApi,
    private val dao: UserDao,
    private val analytics: AnalyticsTracker
) {
    suspend fun loginUser(email: String, password: String): Result {
        return runCatching {
            val token = api.login(email, password)
            val user = User(token.userId, email, token.accessToken)
            dao.saveUser(user.toEntity())
            analytics.track("login_success", mapOf("method" to "email"))
            user
        }
    }
}

AuthService is a Facade that hides AuthApi, UserDao, and AnalyticsTracker from the ViewModel. The UI calls loginUser(email, password) instead of three separate requests to the API, database, and analytics. This reduces coupling: if tomorrow AuthApi becomes FirebaseAuth or UserDao migrates to Room, only the Facade changes, not the UI.

Facade in mobile architecture: Service Layer

Service Layer is a common Facade implementation in mobile applications. It encapsulates business logic and coordination between layers. In Android, the Service Layer is often implemented via UseCase (Clean Architecture), and in iOS via Manager or Service protocols.

Component Role in the subsystem What the Facade hides
AuthApi Network request to the server Request format, endpoint, HTTP error handling
UserDao Local token storage Database schema, SQL queries, migrations
AnalyticsTracker Sending analytics events Firebase/AppMetrica SDK, event format
NetworkMonitor Network availability check ConnectivityManager, BroadcastReceiver

AuthService combines all four components. The ViewModel calls one method without knowing that under the hood a network request, database write, tracking, and network check are happening. When testing, AuthService can be replaced with a mock to verify the entire authentication logic without integrating with real components.

Facade vs Adapter vs Mediator

Facade, Adapter, and Mediator are structural patterns, but they solve different problems. They are often confused because all three introduce a mediator object. Let's look at the differences using a mobile application example.

Aspect Facade Adapter Mediator
Purpose Simplify the subsystem interface Convert an interface Reduce component coupling
Direction One interface → subsystem Client → Adaptee N components ↔ Mediator
Interface change Creates a new, simplified one Converts the existing one Does not change, coordinates
Does the subsystem know about the pattern? No No Yes, communicates through Mediator
Example in mobile development UseCase / Service Layer RecyclerView.Adapter Coordinator in iOS

Facade does not hide the subsystem — the client can access AuthApi directly when needed. Adapter necessarily changes the Adaptee's interface. Mediator coordinates complex interactions between many objects that may not know about each other.

Implementing Facade in Kotlin for Android

Facade implementation in Kotlin for Android with Clean Architecture uses UseCase as an entry point for each business scenario. UseCase is a Facade that hides the repository, mapper, and other dependencies from the UI layer.

Kotlin
// Repository is also a Facade, but at a lower level
class UserRepositoryImpl(
    private val local: UserLocalDataSource,
    private val remote: UserRemoteDataSource,
    private val mapper: UserMapper
) : UserRepository {
    override suspend fun getUserProfile(id: String): UserProfile {
        val cached = local.getUser(id)
        if (cached != null && !cached.isStale) {
            return mapper.toProfile(cached)
        }
        val dto = remote.fetchUser(id)
        val entity = mapper.toEntity(dto)
        local.saveUser(entity)
        return mapper.toProfile(entity)
    }
}

// UseCase — a Facade for a business scenario
class LoadUserProfileUseCase(
    private val repo: UserRepository,
    private val analytics: AnalyticsTracker
) {
    suspend operator fun invoke(userId: String): Result {
        return runCatching {
            val profile = repo.getUserProfile(userId)
            analytics.track("profile_loaded", mapOf("user_id" to userId))
            profile
        }
    }
}

LoadUserProfileUseCase is a Facade for the profile loading scenario. It hides caching logic (local → remote), DTO → Entity → Profile mapping, and analytics tracking. The ViewModel calls invoke(userId) and receives a ready UserProfile or an error. UseCase can be tested in isolation by replacing the repository with a mock object.

Implementing Facade in Swift for iOS

Facade in iOS is often implemented as a Manager or Service. Unlike Android, iOS uses protocols to define the Facade interface, making it easy to swap implementations in tests. Let's look at a Facade for working with media — loading, caching, and display.

Swift
protocol MediaServiceProtocol {
    func loadImage(from url: URL) async -> Result<UIImage, Error>
}

final class MediaService: MediaServiceProtocol {
    private let cache: ImageCache
    private let downloader: ImageDownloader
    private let decoder: ImageDecoder
    
    func loadImage(from url: URL) async -> Result<UIImage, Error> {
        // 1. Check the cache
        if let cached = cache.image(for: url) {
            return .success(cached)
        }
        // 2. Load data
        let result = await downloader.download(from: url)
        guard case let .success(data) = result else {
            return .failure(MediaError.downloadFailed)
        }
        // 3. Decode
        guard let image = decoder.decode(data) else {
            return .failure(MediaError.decodeFailed)
        }
        // 4. Save to the cache
        cache.setImage(image, for: url)
        return .success(image)
    }
}

MediaService encapsulates a three-step process: cache → load → decode. The UI calls one method loadImage(from:) instead of managing ImageCache, URLSession, and ImageDecoder. When testing, MediaServiceProtocol can be replaced with a mock returning preset images without real loading.

Common mistakes when using Facade

Mistakes in Facade design negate its advantages: instead of simplification, you get a God Object that the whole system depends on. Let's look at the three main problems.

God Facade — too much responsibility

When a single Facade contains methods for authorization, profile loading, message sending, and synchronization — this is a God Object. A sign: 15+ public methods in one class. Solution: split it into several specialized Facades by area of responsibility — AuthService, ProfileService, MessagingService.

Facade with subsystem detail leakage

If a Facade returns types specific to the subsystem (for example, FirebaseUser or RealmObject), the client is still tied to a specific implementation. Solution: the Facade should return only its own types (data class / struct), completely abstracting the client from subsystem details.

Facade as the only entry point

When a Facade forbids direct access to the subsystem, it becomes a bottleneck. Sometimes the client needs a specific subsystem method, and forcing them through the Facade is redundant. Facade should not be a strict gatekeeper: it provides a convenient interface but does not block direct access to components.

Frequently Asked Questions

What is the difference between Facade and Proxy?

Facade provides a simplified interface to the subsystem, often creating a new set of methods. Proxy keeps the same interface as the original object but adds access control or lazy loading. Facade is for simplification, Proxy is for control.

Is Facade the same as Service Layer?

Service Layer is a Facade pattern implementation at the application architecture level. It defines the boundary between UI and business logic, hiding service implementation details. In Android, the Service Layer is often implemented via UseCase; in iOS, via Manager or Service protocols.

When does Facade become a God Object?

God Facade occurs when one class takes responsibility for several unrelated subsystems. Signs: 15+ public methods, methods from different domains (authorization + payments + notifications), a class that is hard to test (10+ dependencies). Solution: split into domain Facades.

Is Facade needed in a small application?

In an app with 1-2 screens, Facade is redundant — calling the API and database directly from the UI is simpler and clearer. Facade pays off with 5+ screens and 3+ subsystems. In small and medium projects, a Repository as the only Facade layer is enough, without an extra UseCase wrapper.

How to test code that uses Facade?

Facade simplifies testing because it replaces an entire subsystem with a single mock object. Instead of mocking three components (network + database + analytics), mocking one Facade is enough. In Swift, protocols are used for this; in Kotlin, interfaces. Facade is also convenient for integration tests that verify component orchestration.

Summary

  • Facade — a structural pattern that provides a simple interface to a complex subsystem
  • Service Layer and UseCase — common Facade implementations in mobile architecture
  • Facade does not hide the subsystem: the client can access components directly when needed
  • Facade vs Adapter: Facade simplifies, Adapter converts; Facade vs Mediator: Facade is one-way, Mediator is two-way
  • God Facade — an antipattern: 15+ methods in one class signal a violation of Single Responsibility
  • Protocol/interface for Facade is a must — it is the only way to mock-test the subsystem
  • Recommendation: introduce Facade with 5+ screens and 3+ subsystems; for small projects, Repository is enough

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also