Why does EK9 reject two 'register' lines for the same abstract component type?
← DI Validation · Ref: Q1334
Within a single application block each abstract component type may have exactly ONE concrete registration. A second 'register ... as <SameAbstractType>' raises E08230 (duplicate registration). The reason is resolution: when a program injects 'cache as Cache!', the compiler must bind that injection point to exactly one concrete implementation. Two registrations for Cache would make that binding ambiguous, so EK9 rejects it at compile time rather than picking arbitrarily or failing at runtime.
The fix is to keep one binding per abstract type in the application. If you genuinely need a different implementation (production vs test), declare a SEPARATE application, each with its own single registration, and select the application with 'with application of'.
See Q673 for missing registration (E08210). See Q674 for the same rule from the ambiguity angle. See Q671 for circular dependencies.
Example
defines module qa.divalidation.duplicateregistration defines component <?- Abstract cache contract. -?> Cache as abstract store() as abstract -> key as String payload as String default operator ? <?- Redis-backed cache for production. -?> RedisCache extends Cache override store() -> key as String payload as String stdout <- Stdout() stdout.println(`Redis store ${key}=${payload}`) default operator ? <?- In-memory cache for tests. -?> MemoryCache extends Cache override store() -> key as String payload as String stdout <- Stdout() stdout.println(`Memory store ${key}=${payload}`) default operator ? defines application <?- Production wiring: exactly ONE registration for Cache. -?> ProdApp register RedisCache() as Cache <?- Test wiring: same abstract type, different implementation, in a SEPARATE application. One binding each, no E08230. -?> TestApp register MemoryCache() as Cache defines program DuplicateRegistrationDemo() with application of ProdApp stdout <- Stdout() cache as Cache! cache.store("user:1", "Alice") stdout.println("One abstract type = one registration per application")
Common mistakes
E08230 — Both 'register' lines bind the same abstract type Cache inside one application, so an injection point 'cache as Cache!' has two candidate implementations and cannot be resolved unambiguously - this raises E08230. Keep one registration per abstract type per application; if you need a different implementation, put it in a separate application (ProdApp vs TestApp) and select it with 'with application of'. See ek9 -h E08230 for details.
Incorrect:
BrokenApp
register RedisCache() as Cache
register MemoryCache() as Cache
Correct:
ProdApp
register RedisCache() as Cache
<?-
Other ways to ask this
- What triggers E08230 duplicate registration in an application?
- Why can I only register one implementation per abstract type per application?
- How do I wire two implementations of one interface in EK9?
Coming from another language?
Java/Spring: two beans of one type need @Primary or @Qualifier, otherwise NoUniqueBeanDefinitionException at startup (runtime). Guice: a duplicate bind throws CreationException at injector build time. Kotlin/Koin: last definition silently overrides earlier ones. EK9: a duplicate registration for the same abstract type in one application is a compile-time error (E08230); use separate applications for separate wiring.
Keywords: registration, component, register, DI, binding, E08230, application, inject, ambiguous, duplicate, abstract