The best approach for adopting composability is starting to create new features in Harmony while leveraging 100% of your existing codebase. You can do that by using existing packages and backend APIs you already maintain, or by packaging your existing app codebase for reuse in Bit components and Harmony aspects.
These are the recommended guidelines for accessing and leveraging existing code while building new composable features:
- Create a new application shell to compose new and existing features.
- Create new features as Harmony aspects
- Compose existing micro services, micro frontends and apps into aspects.
- As needed, create and reuse new UI components, entities and hooks directly in existing projects, modernizing your existing tech stack.
This approach lets you gradually introduce composability into your tech stack without a disruptive rewrite or breaking changes.
Below are specific techniques and references for using app code, micro services and micro frontends in composable features.
If you have a monolithic application containing your features and code you wish to leverage in composable features, follow these steps:
- Publish your app as package, making existing code accessible as a dependency for any new composable feature.
- Create the application shell.
- Create a dev environment, set the package created in step #1 as a peer dependency in env.jsonc.
- Create new features, and as needed, bring the new package that contains your app as a dependency, to use it's existing capabilities.
For example, when your monolithic app is published as a package, you can use any of the public APIs set in the index.js
file you created, or even import internal files:
import { Feature } from '@myorg/legacy.monolithic-app'; // for APIs exposed import Homepage from '@myorg/legacy.monolithic-app/components/Homepage.js'; // for internal files
Now you can start building new features in Bit and Harmony while reusing your existing app code in Bit components!
To leverage micro services or micro-frontends, simply consume them in runtime, or gradually move them to build time as needed to optimize for end-user experience.
Here's an example method using an existing micro service from a new aspect:
// people.node.runtime.ts import { SymphonyPlatformAspect type SymphonyPlatformNode } from '@bitdev/symphony.symphony-platform'; import { User, createUserMock } from '@pied/people.entities.user'; import { peopleGqlSchema } from './people.graphql.js'; export class PeopleNode { constructor( readonly config: PeopleConfig ) {} /** * Define the API to list users and use throughout new features. **/ async listUsers(): Promise<User[]> { const objects = await fetch(`${this.config.legacyUserService}/users/list'`); return objects.map((plainUser) => { return User.from(plainUser); }); } static defaultConfig = { legacyUserService: 'https://api.user-service.com' }; static async provider(_, config) { return new PeopleNode(config); } }
Of course, the frontend can also directly use the existing server if permitted by CORS and security policies.
Use the index.html
file or the webpack app configuration to load existing micro frontends and stitch them into the platform. Find more information about using Microfrontends in Bit apps in the Microfrontends docs section.
For a multi app architecture, hosted behind a proxy server, we recommend following the next steps:
- Create the application shell.
- Create new features in Harmony aspects.
- Deploy the Harmony platform.
- Gradually transition the proxy to point new routes to new Harmony routes.
Optionally, publish your existing apps as packages to reuse in new routes.
Use standard package managers to install components in your existing project:
npm install @pied/people.ui.user-profile
Learn about reusing Bit components in existing projects.