Read-model naming policy
How Arc names read-model collections
Section titled “How Arc names read-model collections”When Arc resolves a MongoDB read-model collection, it delegates to a NamingPolicy bean to convert the JVM type into a storage name. The NamingPolicy interface declares two methods:
getReadModelName(Class<?>)— returns the collection name for a read-model type. Must be non-blank and stable across calls for the same type.getPropertyName(String)— returns the stored field name for a declared member name. The default implementation returns the name unchanged.
interface NamingPolicy { fun getReadModelName(readModelType: Class<*>): String fun getPropertyName(name: String): String // default: returns name unchanged}Default policy
Section titled “Default policy”DefaultNamingPolicy is registered automatically by the MongoDB integration. It pluralises the simple class name using Evo Inflector (org.atteo:evo-inflector).
// Kotlin — uses default (pluralizeReadModelNames = true)class DefaultNamingPolicy(pluralizeReadModelNames: Boolean = true) : NamingPolicy// JavaNamingPolicy policy = new DefaultNamingPolicy(); // pluralisesNamingPolicy flat = new DefaultNamingPolicy(false); // returns simple name as-isSetting pluralizeReadModelNames = false returns the simple class name unchanged.
The Chronicle Kernel is authoritative
Section titled “The Chronicle Kernel is authoritative”Pluralisation on the JVM uses Evo Inflector, a different library from the Humanizer library used by the .NET Chronicle Kernel. The two libraries agree on most common English nouns, but where they disagree on a specific noun the kernel is authoritative: the application must override the naming policy for that type so that the JVM reads from the collection the kernel writes to.
The cases pinned by DefaultNamingPolicyTests record what Evo Inflector actually produces. A library upgrade that changes a plural will fail those tests rather than silently moving a collection name.
Override the policy for a specific type
Section titled “Override the policy for a specific type”Register a Spring bean that implements NamingPolicy and is annotated with @ConditionalOnMissingBean in your autoconfiguration, or supply it directly:
@Configurationclass NamingConfig { @Bean fun namingPolicy(): NamingPolicy = object : NamingPolicy { override fun getReadModelName(readModelType: Class<*>): String = when (readModelType) { // The kernel writes to "PersonProjections"; the JVM inflector would produce "People". PersonView::class.java -> "PersonProjections" else -> DefaultNamingPolicy().getReadModelName(readModelType) } }}// Java equivalent@Configurationclass NamingConfig { @Bean NamingPolicy namingPolicy() { return readModelType -> { if (readModelType == PersonView.class) return "PersonProjections"; return new DefaultNamingPolicy().getReadModelName(readModelType); }; }}The NamingPolicy bean is consulted once per type at lookup time. Implementations are responsible for their own caching when the computation is expensive.