Pluggable PBCs with Bit Harmony

How to standardize and simplify the integration of Packaged Business Capabilities

Eden Ella
Bits and Pieces

--

‘Package Business Capabilities’ (PBCs), a term coined by Gartner, are independently deployable and functionally discrete units of software that align with the business model, objectives, and language.

As the adoption of PBCs becomes more widespread, organizations that master their implementation and integration will find themselves at the forefront of their respective industries, ready to leverage technological progress to drive sustained growth and success.

In this article, we’ll explore Bit Harmony, a tool that has the potential to significantly enhance an organization's leverage of Package Business Capabilities.

Bit Harmony is an innovative platform for the composition of PBCs using smart dependency injection. Using Bit Harmony, fusion teams can compose PBCs with minimal overhead and deliver new experiences or apps, at an accelerated pace while ensuring consistency and reusability.

A composition of the Blog PBC (aspect) into a new product, the “Apparel Waves” online clothing store

To see a demo of this approach visit the ‘Apparel Wave’ scope and the ‘Blog PBC’ scope.

The blog PBC scope that hosts the Blog PBC as well as its dependencies (other Bit components)
The “Apparel Waves” online store solution that hosts the Harmony app and the aspect with the functionalities unique to this solution

Aspects

An aspect is the building block of a Harmony solution. It is a full-stack Bit component that provides UI and backend “services” for other aspects.

In the context of this article, an Aspect represents a single Packaged Business Capability (PBC) that can be plugged into a larger system to form a full solution, a new application.

Aspects extend other aspects by registering to their ‘slot’ API. This inversion of control allows fusion teams to compose PBCs with minimal overhead since the aspect is responsible for the integration, not the system that composes it.

For example, the following Harmony app is an online shop for surfer clothing. The fusion team responsible for that online store decided to add a blog to their site. After searching for a suitable PBC on the Bit platform, they came across this Blog PBC. Deciding they wanted to use it, they added it to their Hamrony application:

/**
* @coponentId: learnbit.apparel-waves/apparel-waves
* @filename: apparel-waves.bit-app.ts
*/

// imports...
import { SymphonyPlatformAspect } from '@bitdev/symphony.symphony-platform';
import { ApparelWavesPlatformAspect } from '@learnbit/apparel-waves.apparel-waves-platform';
import { BlogAspect } from '@learnbit/blog-pbc.blog';

export const ApparelWaves = HarmonyPlatform.from({
name: 'apparel-waves',
platform: [
/**
* ascpects register themsevles to the 'platform' aspect which
* is the entry point for this application
*/
SymphonyPlatformAspect,
{
name: 'Apparel Waves',
slogan: 'Making waves in fashion',
domain: 'apparel-waves.com',
},
],
/**
* aspects can run in multiple runtime environments. here, aspects
* provide functionalitis to the NodeJS services and to the web frontend apps
*/
runtimes: [new BrowserRuntime(), new NodeJSRuntime()],

aspects: [
/* 'apperal waves' aspect extends the system with its
* own unique functionalities. this aspect is maintained by
* a fusion team that composes the PBCs for their own solution.
*/
ApparelWavesPlatformAspect,
/**
* the blog (PBC) aspect extends the system with content
* management capabilities. it is maintained by the blog PBC team.
*/
[
BlogAspect,
{
/**
* the blog aspect also provide a config api for this app to use
* in this case, since the the blog uses the Contenful platform,
* the fusion team need to provide it with their own Contentful space ID
*/
spaceId: 'contentful-spaceId',
},
],
],
});

export default ApparelWaves;
The blog PBC registered to the Harmony platform

The Blog PBC (aspect) registers itself to the platform in several ways:

  1. It extends the system’s GraphQL schema with a node for content retrieval. This is done in the NodeJS Runtime.
  2. It extends the system’s routing with the /blog route. This is done in the Browser Runtime.
  3. It extends the header with an additional item a ‘Blog’ link to /blog . This is done in the Browser Runtime.

NodeJS Runtime

/**
* @coponentId: learnbit.blog-pbc/blog
* @filename: blog.node.runtime.ts
*/

export class BlogNode {
constructor(private config: BlogConfig) {}

async getBlogPosts() {
const blogData = new BlogData(this.config);
return blogData.getBlogPosts();
}

static dependencies = [SymphonyPlatformAspect];

static async provider(
[symphonyPlatform]: [SymphonyPlatformNode],
config: BlogConfig
) {
const blog = new BlogNode(config);
const gqlSchema = blogGqlSchema(blog);

symphonyPlatform.registerBackendServer([
{
gql: gqlSchema,
},
]);

return blog;
}
}

export default BlogNode;

Browser Runtime

/**
* @coponentId: learnbit.blog-pbc/blog
* @filename: blog.browser.runtime.ts
*/


export class BlogBrowser {
constructor(private config: BlogConfig) {}

static dependencies = [SymphonyPlatformAspect, HeaderAspect];

static async provider(
[symphonyPlatform, header]: [SymphonyPlatformBrowser, HeaderBrowser],
config: BlogConfig
) {
const blog = new BlogBrowser(config);

symphonyPlatform.registerRoute([
{
path: '/blog',
component: () => {
return (
<ApolloBlogProvider spaceId={config.spaceId}>
<BlogLobby />
</ApolloBlogProvider>
);
},
},
]);

header.registerLink([{ label: 'Blog', href: '/blog' }]);

return blog;
}
}

export default BlogBrowser;

PBCs are encouraged to be a hybrid of off-the-shelf (bought) focused solutions composed together by new code instead of the traditional dichotomy between “buy or build.”

The blog PBC applies this recommendation by using the Contentful content management system. It offers a coherent “language” for bought services within the company’s PBC ecosystem, ensuring they can communicate effectively and function seamlessly together.

/**
* @coponentId: learnbit.blog-pbc/blog
* @filename: blog-data.ts
*/

import { ApolloClient, InMemoryCache, HttpLink, gql } from '@apollo/client';
import type { BlogConfig } from './blog-config.js';

export class BlogData {
constructor(private readonly config: BlogConfig) {}

private contentfulClient = new ApolloClient({
link: new HttpLink({
uri: `https://graphql.contentful.com/content/v1/spaces/${this.config.spaceId}`,
headers: {
Authorization: `Bearer ${process.env.CONTENTFUL_ACCESS_TOKEN}`,
},
fetch,
}),
cache: new InMemoryCache(),
});

getBlogPosts = async () => {
const { data } = await this.contentfulClient.query({
query: gql`
query GetBlogs {
pageBlogPostCollection {
items {
title
slug
author {
name
}
}
}
}
`,
});
return data.pageBlogPostCollection.items.map((item) => ({
title: item.title,
slug: item.slug,
author: {
name: item.author ? item.author.name : null,
},
}));
};
}

--

--