Skip to content

Read-model naming policy

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 {
String getReadModelName(Class<?> readModelType);
default String getPropertyName(String name) {
return name;
}
}

View Java snippet source on GitHub

DefaultNamingPolicy is registered automatically by the MongoDB integration. It pluralizes the simple class name using Evo Inflector (org.atteo:evo-inflector), except for a small built-in list of nouns where Evo Inflector and the kernel disagree: Person becomes People and Index becomes Indices, matching the kernel.

NamingPolicy pluralising = new DefaultNamingPolicy();
NamingPolicy asDeclared = new DefaultNamingPolicy(false);

View Java snippet source on GitHub

Setting pluralizeReadModelNames = false returns the simple class name unchanged.

With the Chronicle integration (io.cratis:arc-chronicle-spring-boot-starter, or io.cratis:cratis) next to the MongoDB integration, Arc’s NamingPolicy also names the collection Chronicle projects each read model into. The integration adapts the single NamingPolicy bean into a Chronicle ReadModelNamingPolicy bean, and the Chronicle Spring Boot starter gives that bean to the ChronicleOptions it builds. A projected Author therefore lands in Authors, the collection Arc reads by default, with no configuration:

  • The default DefaultNamingPolicy flows through, and so does a custom NamingPolicy bean that replaces it.
  • The policy names only the collection. The read model identifier, and everything keyed on it (projections, reducers, observers), is unchanged, and an explicit @ReadModel(id = ...) no longer changes the collection name.
  • Declare your own ReadModelNamingPolicy bean to name Chronicle’s collections differently. It wins over the adapter, and Arc keeps reading through its NamingPolicy, so keep the two consistent.
  • Without a NamingPolicy bean, which means without the MongoDB integration, Chronicle keeps naming the collection after the read model identifier.
  • An application that declares its own ChronicleOptions bean builds the options itself, so it must call withReadModelNamingPolicy there.
@Configuration
public class ChronicleNamingConfig {
// Names only the collections Chronicle projects into; Arc keeps reading through its own NamingPolicy.
@Bean
public ReadModelNamingPolicy readModelNamingPolicy() {
return readModelClass -> "projected_" + readModelClass.getSimpleName();
}
}

View Java snippet source on GitHub

Existing data does not follow a renamed collection. Chronicle does not move or delete documents when a read model’s collection name changes, so a read model already projected under an earlier name stays in its old collection. Replay the projection to fill the new collection, and drop the old one yourself when you no longer need it.

The Chronicle Kernel is authoritative for .NET clients

Section titled “The Chronicle Kernel is authoritative for .NET clients”

Pluralization on the JVM uses Evo Inflector, a different library from the Humanizer library used by the .NET Chronicle client. The two libraries agree on most common English nouns, but where they disagree on a specific noun, a collection written by a .NET client follows Humanizer. The built-in corrections cover only the nouns measured so far; for any other noun that differs, override the naming policy for that type so that Arc reads the collection the .NET client writes to. Read models projected through the JVM Chronicle integration need no such correction, because Chronicle then uses Arc’s policy for the collection name.

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.

Register a Spring bean that implements NamingPolicy and is annotated with @ConditionalOnMissingBean in your autoconfiguration, or supply it directly:

@Configuration
class NamingConfig {
@Bean
NamingPolicy namingPolicy() {
return readModelType -> {
// The kernel writes to "PersonProjections"; the JVM inflector would produce "People".
if (readModelType == PersonView.class) return "PersonProjections";
return new DefaultNamingPolicy().getReadModelName(readModelType);
};
}
}

View Java snippet source on GitHub

Arc calls the NamingPolicy bean on every collection lookup and does not cache the result. Keep getReadModelName cheap and deterministic, and cache inside your implementation when the computation is expensive.