HarmonyOS Componentization Without Reflection: Contracts, Registries, and Unidirectional Dependencies
ArkTS lacks reflection, so standard Android-style service discovery patterns do not apply. This project provides a concrete, battle-tested blueprint for decoupling HarmonyOS apps that compiles cleanly and surfaces missing registrations at runtime rather than through cryptic crashes.
A complete HarmonyOS sample app splits login, payment, and forced-update features into isolated HAR and HSP packages that communicate only through a shared API layer. The architecture enforces three rules: package-type isolation, interface contracts, and unidirectional dependencies where business modules never import each other. Four communication mechanisms replace reflection, which ArkTS does not support: a ServiceRegistry for request-response calls, a RouteCenter for page navigation, AppStorage wrapped in an HSP for global reactive state, and an emitter event bus for fire-and-forget notifications. Each business module initializes by explicitly registering its service implementations and page builders, making the dependency graph traceable at compile time. A mock HTTP layer sits behind the service interfaces so that swapping in real network calls requires zero changes to business code. The project also documents ten specific build and configuration pitfalls encountered during development, from missing module.json5 files to illegal hex literals and incorrect system resource names.
Explicit registration at startup is a pragmatic substitute for dependency injection in a reflection-less runtime, and it makes missing dependencies immediately visible as undefined services or unregistered pages.
The HSP-vs-HAR distinction for stateful singletons is a subtle but critical design constraint: getting it wrong silently corrupts app state rather than producing a clear error.
Reserving the event bus strictly for fire-and-forget notifications and routing all request-response traffic through a typed registry prevents the debugging nightmare of implicit, untraceable call chains.
The forced-update dialog bypasses the RouteCenter entirely because it is a component, not a page, which clarifies the boundary between component-level reuse and page-level navigation.
I've also studied HarmonyOS componentization before, following the official recommendations, which is relatively simple.
This lacks the ability to freely switch between application and library like on Android, so it still falls a bit short.