FLIP: Modular Architecture for KMP

FLIP: Modular Architecture for KMP

1 8
calendar_todayschedule2 min read

While building haskcore - an IDE for Haskell on Compose Desktop - I extracted rules that allowed me to create an architecture of 40 isolated modules, where each module knows only what it needs.

That's how FLIP - Feature-Layered Isolated Platform - was born.

The main problem I faced during development: horizontal dependencies make the system fragile. The solution - strict vertical hierarchy.

FLIP layers

FLIP consists of the following isolated modules:

  • :common:core - common logic components.
  • :common:presentation - common UI components.
  • :service - UI-less domain module. Provides a service interface.
interface SeedService : AutoCloseable {
    val seed: Flow<Seed>
    suspend fun generateSeed(): Either<Throwable, Unit>
}
  • :feature:core — logic module of a UI-feature. Knows nothing about other features. Uses services from :service. Provides use cases.
class GenerateRemoteRandomSeed(private val seedService: SeedService) : UseCase.Action {
    override suspend fun Raise<Throwable>.action() = seedService.generateSeed().bind()
}
  • :feature:presentation — UI module of a UI-feature. Knows only about its :feature:core and uses its use cases. Provides the view. Knows only about its own :feature:core.

  • :entrypoint — module where dependency configuration and composition of views from :feature:presentation into a single Composable happens.

  • :platform — module that initializes the Composable provided by :entrypoint for each platform.

FLIP dependencies

Arrows show the direction of dependencies.

How to structure your system?

Decide which features your app should have, then determine whether each feature has a UI:

  • If it does — it’s a feature.
  • If it doesn’t — it’s a service.
  • If one feature’s logic is needed by another — extract it into a separate service.

Important: both :service and :feature:core contain domain logic:

  • :service — core domain.
  • :feature:core — presentation domain (use cases).

Wrong:

  • :service:foo
  • :feature:foo:core
  • :feature:foo:presentation

Right:

  • :service:foo
  • :feature:bar:core
  • :feature:bar:presentation

Navigation in FLIP is just another feature. It aggregates and routes views passed as lambda slots, keeping it independent from other features.

NavigationView(
    splash = {
        SplashView(applicationScope = applicationScope)
    },
    profile = {
        ProfileView(applicationScope = applicationScope)
    }
)

This approach lets you add new screens without touching existing features.

A complete example is available in the official GitHub repository.

225 Points9 Badges1 8
4Posts
2Comments
2Followers
2Connections
Software Architect & Engineer. Building tools that simplify complexity.
Build your own developer journey
Track progress. Share learning. Stay consistent.

4 Comments

1 vote
0
1 vote
2
🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

The Sovereign Vault — A Comprehensive Guide to Protocol-Driven AI

Ken W. Algerverified - Jun 4

JetBrains Just Changed KMP Structure. Here's What They Didn't Tell You.

numq - May 18

Kotlin Professional Certificate by JetBrains

Mark Kazakov - May 20

Type-safe Kotlin Multiplatform i18n: auto-convert Android strings to cross-platform translations.

Ismoy - Oct 14, 2025

ImagePickerKMP 1.0.23: Controling Camera Launch on iOS with directCameraLaunch

Ismoy - Sep 3, 2025
chevron_left

Related Jobs

View all jobs →

Commenters (This Week)

2 comments
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!