Migrating Layer Access
This guide moves existing applications from global string access to concrete layer references and local aliases. Compatibility APIs remain available so the migration can be incremental.
Target Model
const app = await createApp(App).useLayers([AuthLayer]);
const Profile = define({
layers: { auth: AuthLayer } as const,
script({ layers: { auth } }) {
return {
user: auth.service('auth').currentUser(),
session: auth.prop('session'),
};
},
template: ({ user }) => <p>{user?.name}</p>,
});
The composition root owns initialization. The component owns its local alias. The concrete layer object carries service and prop types between them.
1. Rename provides To services
Before:
defineLayer({
name: 'auth',
provides: { auth: () => authService },
});
After:
export const AuthLayer = defineLayer({
name: 'auth',
services: { auth: () => authService },
});
provides remains a compatibility alias. Do not mix provides and services
for the same capability key.
2. Replace String Service Access
Before:
script({ useStore, useService }) {
const auth = useStore('auth');
const session = useService('session');
}
After:
layers: { auth: AuthLayer } as const,
script({ layers: { auth } }) {
const authService = auth.service('auth');
const session = auth.prop('session');
}
For a narrow transitional call, useService(AuthLayer, 'auth') preserves
concrete-layer typing without adding a local alias. Alias records remain the
preferred shape when a component consumes more than one layer member.
3. Replace Hook Dependencies
Before:
defineHook({
deps: ['auth'],
setup({ layerProvider }) {
return layerProvider('auth');
},
});
After:
defineHook({
layers: { auth: AuthLayer } as const,
setup({ layers: { auth } }) {
return auth.service('auth');
},
});
Hooks and components now have the same dependency vocabulary and missing-layer diagnostics.
4. Remove Registry Augmentation
Delete EffuseLayerRegistry declarations after all consumers infer their
contract from concrete layer objects. Keep augmentation only while an unmigrated
string call still needs it.
5. Move Server Work Into Owning Layers
Move disconnected handlers into server.api or server.actions, or adapt
existing files with fromServerFiles. Server handlers then infer the same
services used by components and appear in createLayerServerManifest.
6. Verify The Composition Root
Every declared consumer layer must be registered before client mount or server request handling. Run nested and lazy routes during migration; Effuse reports the component or hook name, alias, concrete layer, and registration fix when a binding is missing.
Migration Checklist
- Each domain exports one concrete layer object.
- New service declarations use
services. - Components and hooks use alias records.
- String
useStore,useService, and layer prop calls are removed. - Registry augmentation is deleted when no longer needed.
- Server APIs/actions are owned by layers or adapted into them.
- The same layer graph is passed to client, SSR, handler, and manifest setup.
- Typecheck, focused tests, and lazy-route integration probes pass.
Next Steps