Skip to main content

Migration guide

Work in progress

The Evolve Framework is an ongoing, backwards-compatible refactoring. Not all services have been migrated yet, and APIs may still evolve. Everything described here is functional but should be considered pre-release.

This guide walks through migrating an existing Evolve service to use the framework packages. The examples use @evolve-framework/commercetools, but the same steps apply to other implementation packages. You can migrate one service at a time; unmigrated services continue to work as before.

Before you start

Make sure the vendor/packages/ directory with the three framework packages is available in your workspace. The packages are distributed as part of the Evolve platform and do not need to be installed from a registry.

Step 1: update dependencies

Add the framework package and remove the packages it replaces:

  "dependencies": {
+ "@evolve-framework/commercetools": "workspace:*",
- "@evolve-packages/http-utils": "workspace:*",
- "@evolve-packages/secrets": "workspace:*",
- "@evolve-packages/types": "workspace:*",
- "dataloader": "...",
- "@labdigital/dataloader-cache-wrapper": "...",
- "libphonenumber-js": "...",
- "zod": "..."
}

The framework re-exports everything these packages provided. Your service gets them transitively through @evolve-framework/commercetools.

Step 2: update workspace configuration

Add the vendor packages directory to your pnpm-workspace.yaml:

packages:
- "frontend/*"
- "packages/*"
- "vendor/packages/*" # Add this line

Step 3: simplify initialization

Replace local client factory and token manager setup with the framework's configured versions:

- import { ClientFactory } from "./commercetools.ts";
- import { TokenManager } from "./core/tokens.ts";
+ import {
+ configureClientFactory,
+ configureTokenManager,
+ } from "@evolve-framework/commercetools";

export const initEnvironment = async () => {
await loadConfig();
await cache.configure(config.REDIS_URL);
- clientFactory = new ClientFactory(config);
- tokenManager = new TokenManager(config);
+ await configureClientFactory(config);
+ await configureTokenManager(config);
};
info

Not every service needs configureTokenManager. Only the account service (which handles authentication directly) requires it. Other services use federated auth and only need configureClientFactory.

Step 4: compose modules

Replace local resolver and schema imports with framework modules. Pass module configuration where needed (see module configuration for details):

- import { resolvers } from "./resolvers/index.ts";
- import { typeDefs } from "./graphql/schema.ts";
+ import {
+ CustomerGraphQLModule,
+ GraphQLCompositeModule,
+ } from "@evolve-framework/commercetools";
+
+ const module = new GraphQLCompositeModule([
+ new CustomerGraphQLModule(),
+ ]);

export const createApp = () =>
new DomainService({
+ name: config.COMPONENT_NAME,
graphql: {
- typeDefs: typeDefs,
- resolvers: resolvers,
+ typeDefs: module.getTypedefs(),
+ resolvers: module.getResolvers(),
context: newContext,
+ plugins: [useClientContext()],
},
+ http: {
+ address: { host: config.HTTP_HOST, port: config.HTTP_PORT },
+ },
});

See the available modules table for which module to use per service.

Step 5: simplify context

Replace local dataloader and client context imports with the framework's versions. The approach depends on your service's authentication model.

Federated auth (most services): use initContextValue to let the framework create all dataloaders:

- import { createDataLoaders } from "./dataloaders/index.ts";
- import { ClientContextLoader } from "./client-context.ts";
+ import {
+ initContextValue,
+ RemoteClientContextLoader,
+ clientFactory,
+ } from "@evolve-framework/commercetools";
+ import { readStoreContextFromRequest } from "@evolve-framework/core";

export const newContext = async ({ request }) => {
- const loader = new ClientContextLoader(config);
+ const storeContext = readStoreContextFromRequest(request);
+ const loader = new RemoteClientContextLoader(
+ config.ACCOUNT_SERVICE_ENDPOINT,
+ clientFactory,
+ );
+ const clientContext = new ClientContext(storeContext, loader);
+ const context = { storeContext, clientContext };
+ initContextValue(context);
+ return context;
};

Direct auth (account service only): create dataloaders manually:

- import { createDataLoaders } from "./dataloaders/index.ts";
- import { ClientContextLoader } from "./client-context.ts";
+ import {
+ createDataLoaders,
+ CommercetoolsClientLoader,
+ clientFactory,
+ } from "@evolve-framework/commercetools";
+ import { readStoreContextFromRequest } from "@evolve-framework/core";

export const newContext = async ({ request }) => {
+ const storeContext = readStoreContextFromRequest(request);
- const loader = new ClientContextLoader(config);
+ const loader = new CommercetoolsClientLoader(clientFactory);
+ const clientContext = new ClientContext(storeContext, loader);
+ return {
+ storeContext,
+ clientContext,
+ loaders: createDataLoaders(/* ... */),
+ };
};

See context and store context for more details on these two patterns.

Step 6: update the entry point

Replace your custom process lifecycle code with ProcessManager:

+ import { ProcessManager } from "@evolve-framework/core";
+ import { cache } from "@evolve-framework/core/cache";

+ let app: DomainService;
+
+ const pm = new ProcessManager({
+ start: async () => {
+ await initEnvironment();
+ app = createApp();
+ await app.start();
+ },
+ stop: async () => {
+ await app?.stop();
+ await cache.close();
+ },
+ });
+
+ await pm.start();

ProcessManager handles SIGTERM/SIGINT for graceful shutdown and catches startup errors.

Step 7: remove migrated files

Delete the directories and files that the framework now provides:

  • src/resolvers/
  • src/dataloaders/
  • src/mappers/
  • src/core/
  • src/types.ts
  • src/commercetools.ts
  • src/testing/test-server.ts

Your service should be left with init.ts, server.ts, context.ts, and any project-specific code (REST endpoints, event handlers, jobs).

Step 8: remove tsconfig path aliases

If your service used a #src/* path alias in tsconfig.json, remove it. Imports now come from framework packages instead of local paths.

Build order

The framework packages must build before your service. The dependency chain is:

@evolve-framework/core
→ @evolve-framework/schemas
→ @evolve-framework/commercetools
→ your service

Your build tooling (pnpm workspaces, turborepo) handles this automatically as long as the workspace:* dependency is declared.

GraphQL codegen

Each service keeps its own codegen.ts configuration and .graphql schema files. The framework's types are pre-generated and included in the @evolve-framework/commercetools package, so you do not need to run codegen for framework types.

Your service's codegen only needs to cover project-specific schema extensions.

Environment variables

The framework does not introduce any new environment variables. Your existing configuration continues to work as-is.

Gateway

The GraphQL gateway is not yet migrated to the framework and is not affected by service-level migrations. No changes to gateway configuration are needed.