Why Resolvers?

The Factory system (Day 15) has a line like { class = "Room", ... }. Something needs to know that “Room” means “build four walls, a floor, and a ceiling.” Yesterday’s cathedral hardcoded that mapping. Today we made it pluggable.

The Resolver pattern is a registry where you register class names and their builder functions:

Resolver.register("Room", function(entry, context)
    -- Build walls, floor, ceiling from entry.size and entry.position
    return parts
end)

Resolver.register("Door", function(entry, context)
    -- Cut a doorway between two rooms
    return parts
end)

The Factory asks the Resolver “how do I build this class?” and gets back a function. This means:

  • New geometry types are additive. Register a builder, done. No editing the Factory.
  • Game-specific geometry stays in game code. A dungeon game registers “CaveRoom” and “TrapDoor.” A house builder registers “Kitchen” and “Bathroom.” Same Factory, different Resolvers.
  • Resolvers compose. A “MansionFloor” resolver can internally use the “Room” and “Door” resolvers to build its sub-rooms. Each level of abstraction resolves to the next level down.

We also built a ClassResolver — a specialized resolver that handles Roblox Instance classes directly. Need a Part? A WedgePart? A UnionOperation? The ClassResolver knows how to create them with sensible defaults (Anchored = true, CanCollide = true, proper material) so individual builders don’t repeat boilerplate.

Atomic Ranch

To test the system, we built a complete modular house layout: the Atomic Ranch. A mid-century modern ranch house with living room, kitchen, bedrooms, bathroom — each defined as a room volume with connecting doors.

The key feature tested here was global openings. Instead of each room defining its own doors and windows (which leads to misalignment when two rooms share a wall), openings are defined at the layout level:

openings = {
    { type = "Door", between = {"kitchen", "living_room"}, width = 4 },
    { type = "Window", room = "bedroom", wall = "north", width = 6 },
}

A door between the kitchen and living room is defined once. The system automatically identifies the shared wall, calculates the cut position, and carves both sides. No duplicate definitions, no risk of a door on one side not lining up with the other.

The Resolver pattern passed the test. The Factory never knew it was building a house — it just resolved classes and called builders. The “house” knowledge lived entirely in the registered resolvers and the layout definition.