Vesselize — A JavaScript IoC Container that Works Seamlessly with Vue.js and React

I am a software engineer who uses both Vue.js and React. Technologies like Vue Composition API and React Hooks have really saved my life.

On the other hand, due to the very complex data processing tasks in the application I maintain, I wrote a lot of service classes to help me complete these tasks. But I found something inconvenient is that I have to manually manage the initialization of these classes and their dependencies.

I have used server-side frameworks such as Nest and Spring, and technologies like dependency injection have solved this problem well. So I was thinking, is there a way to combine this design pattern with Vue.js and React.

The Vesselize framework is a result of my practice. It is a lightweight IoC container inspired by Nest that can seamlessly integrate with Vue.js and React applications. If you are interested, you can refer to this complete user guide.

The following are the core concepts of Vesselize and how to use it.

Vesselize Core Concepts

Providers

In Vesselize, providers are generally constructors that can be instantiated. It can also be any factory method and declared values. They will all be registered in the container for dependency lookup and injection.

Container

The responsibility of the container is to initialize the instances and resolve their dependencies.

Context

By default, instances are singletons. By specifying a context object, we can also create an instance bound to this context.

Getting Started with Vesselize and Vue.js

Below we use code to demonstrate how to use Vesselize in a Vue.js application.

Installation

yarn add @vesselize/vue
# OR
npm i @vesselize/vue

Create Providers

We have the following three service classes:

  • UserAPI is used to get data from the server.
  • UserService calls UserAPI to get data and process the data.
  • AuthService is used to determine the role of the user, such as whether it is an administrator.
// file: api/UserAPI.js
class UserAPI {
async fetchUser(id) {
return fetch(`/path/to/user/${id}`).then(res => res.json());
}
}

// file: services/UserService.js
class UserService {
userAPI = null;

async getUser(id) {
const user = await this.userAPI.fetchUser(id);

// data processing stuff...

return user;
}

// Inject userAPI instance through vesselize
setVesselize(vesselize) {
this.userAPI = vesselize.get('UserAPI');
}
}

// file: services/AuthService.js
class AuthService {
constructor(maxAdminUserId) {
this.maxAdminUserId = maxAdminUserId;
}

isAdmin(user) {
return user.id <= this.maxAdminUserId;
}
}

Create Vesselize Plugin

The following code uses createVesselize to create a Vue.js plugin, which is also a container.

import { createVesselize } from '@vesselize/vue';
import UserAPI from './api/UserAPI';
import UserService from './services/UserService';
import RoleAuthService from './services/RoleAuthService';

const vesselize = createVesselize({
providers: [
{
token: 'UserAPI',
useClass: UserAPI
},
{
token: 'UserService',
useClass: UserService
},
{
token: 'AuthService',
useFactory() {
const maxAdminUserId = 1;

return new AuthService(maxAdminUserId);
}
}
]
});

Use Vesselize Plugin

import { createApp } from 'vue';
import router from './router';
import store from './store';
import vesselize from './vesselize';
import App from './App.vue';

const app = createApp(App)
.use(store)
.use(router)
.use(vesselize);

app.mount('#app');

Acquire Instance in Component

Through the useInstance Composition API, service instances can be obtained in components.

<template>
<div>Profile</div>
<p>{{ JSON.stringify(user) }}</p>
<p>Role: {{ isAdmin ? 'Administrator' : 'User' }}</p>
</template>

<style scoped></style>

<script>
import { computed, ref, watchEffect } from 'vue';
import { useRoute } from 'vue-router';
import { useInstance } from '@vesselize/vue';

export default {
setup() {
const route = useRoute();
const userId = computed(() => route.params.id);
const user = ref({});
const isAdmin = ref(false);
// Inject instances through Vue Composition API
const userService = useInstance('UserService');
const authService = useInstance('AuthService');

watchEffect(() => {
if (userId.value) {
userService.getUser(userId.value).then((data) => {
user.value = data;
isAdmin.value = authService.isAdmin(data);
});
}
});

return {
user,
isAdmin,
};
},
};
</script>

Finally, if you want to try vesselize with Vue.js directly, here is a complete sample project: vesselize-vue-starter.

Getting Started with Vesselize and React

Let’s take a look at how the same example can be implemented in React.

Installation

yarn add @vesselize/react
# OR
npm i @vesselize/react

Create Providers

The same UserAPI, UserService, AuthService service classes as above.

Combine Providers

Combine all providers into an array.

import UserAPI from './api/UserAPI';
import UserService from './services/UserService';
import RoleAuthService from './services/RoleAuthService';

const providers = [
{
token: 'UserAPI',
useClass: UserAPI
},
{
token: 'UserService',
useClass: UserService
},
{
token: 'AuthService',
useFactory() {
const maxAdminUserId = 1;

return new AuthService(maxAdminUserId);
}
}
];

export default providers;

Add VesselizeProvider

Use VesselizeProvider to wrap your App component.

import { VesselizeProvider } from '@vesselize/react';
import providers from './providers';
import UserProfile from './components/UserProfile';

function App() {
return (
<VesselizeProvider providers={providers}>
<UserProfile />
</VesselizeProvider>
);
}

export default App;

Acquire Instance in Component

Through the useInstance hook, service instances can be obtained in components.

import { useParams }  from 'react-router-dom'
import { useState, useEffect } from 'react';
import { useInstance } from '@vesselize/react';

function UserProfile() {
const { id } = useParams();
const [user, setUser] = useState({});
const [isAdmin, setIsAdmin] = useState(false);
// Inject instances through hook
const userService = useInstance('UserService');
const authService = useInstance('AuthService');

useEffect(() => {
userService.getUser(id).then((data) => {
setUser(data);
setIsAdmin(authService.isAdmin(data));
});
}, [id, userService, authService]);

return (
<div>
<span>{JSON.stringify(user)}</span>
<p>Role: {isAdmin ? 'Administrator' : 'User'}</p>
</div>
);
}

export default UserProfile;

Finally, this is the code repository for the sample project created by create-react-app: vesselize-react-starter.

Final Thoughts

I have learned a lot by creating Vesselize, and I hope it can be helpful to you.

Thanks for reading, have a nice day!

Github repository: https://github.com/vesselize

Documentation: https://vesselize.js.org

The following are the core concepts of Vesselize and how to use it.

Try it yourself.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store