Midway v1.0 community version release — A solution for future full-stack

Lellansin
6 min readMar 3, 2019

--

Previously, we opened our process manager the Pandora.js toolkit for monitoring and management of the entire Node.js application lifecycle to the community. And we promised it’s not the end.

Facing the increasing of full-stack applications and the increasing complexity of Alibaba’s business, such as online shop beautify, page builder and page rendering. With the continuous status that programmers come and go, the change of products structure, the constant adjustments of code layers, we urgently need a solution that can reduce the complexity of the code, which puts different requirements on our internal infrastructure system.

In the past, what we need was to let the user run the server and deal with the RPC/HTTP protocol [1]. But it seems that there is not enough practice and think to the true full-stack development. So we redesigned the entire MidwayJS solution, and proposed to open source to the community at the beginning.

With the practice of Pandora.js which uses Typescript, we got confidence to rewrite the Midway Framework with TypeScript. At the same time, with the growing of Egg.js community, we believe there will be special way for different scenes.

Since these considerations, Midway introduced the IoC and custom decorator which inspired from NestJs to enhanced development experience,

and decided to cooperate with other products, like Pandora.js and Sandbox, to change the development experience of Node.js and happy coding.

I would like to thank the contributors who gave commits and suggest during the previous beta test. Thank you for your inclusiveness and encourage, especially the active contributors of @ZQun and @yuu2lee4.

Here are some of the features of the new version of Midway.

  • Decoupling business code and unified management & initialization of dependences by IoC mechanisms.
  • Decorator that can simplifies development for common web scene.
  • Support all plugin mechanisms of Egg.js, with unified coding style by framework’s decorator.
  • Interface-oriented programming experience based on TypeScript.

To inject dependency

A year ago, our business logic was heavily coupled, classes initialized everywhere, and full of repeated instances. But it's not the problem of the person who write these business logic did the bad architecture. Actually, under heavily constant work iterations, we had messed up the original idea. What turned out is we found our design of the code can’t follow with the highly increasing demand.

Therefore, we try to introduce a dependency injection scheme. Dependency injection was first heard on the spring framework of Java. In terms of JS, we used XML as the foundation of the IoC solution in first version. Although it solved many coupling and initialization problems, we did receive the complaint of frontend guys about the bad feelings with XML.

After growing of Typescript last year, many on-premise projects have been tried it in production. And in our research, there seems to be few products which were highly extensible except NestJs and the famous Inversify.

So we did a lot of work about it. On the one hand, do more jobs about extensible which could support current demand with decorators and requestContext etc. On the other hand, improve framework structure and convenience for progressive iterations.

We published the library named injection as the foundation of a dependency injection for framework.

Now, injection supports the entire system of Midway, which combines framework code, business code, plugins, and more, like a link to all the data through.

Through the management of dependency injection containers, the very complex applications shown above can be well maintained and operated.

To see the full picture, you can click here .

Decorator oriented development

Thanks to the good support of Typescript for ES6, there’s a new way to annotations and meta programming syntax to class declarations & members. The decorator, which experimental feature of TypeScript, allow us to simplify the structure in coding. Although it’s just syntactic sugar, it brings a lot of benefits.

Let’s take a simple example, about a HTTP Controller that get data from the database of a Service/Manger, step by step. In the multi-layered architecture, the code is highly possible that need new different instances and then bind them in a router file in previous version.

export = (app) => {
const home = new HomeController();
app.get('/', home.index);
}

class HomeController extends Controller {

reportService: IReportService;

constructor() {
this.reportService = new ReportService();
}

async index(ctx) {
ctx.body = await this.reportService.getReport();
}
}

class ReportService implements IReportService {

reporter: IReportManager;

constructor() {
this.reporter = new ReporterManager();
}


async getReport(id: number) {
return await this.reporter.get(id);
}
}

class ReporterManager implements IReportManager {

db;

constructor() {
this.initDB();
}

initDB() {
// open connection
}

async get() {
// return data from db;
}
}

After the annotated by @provide and @inject and other web decorators of IoC, it's not just reduced of the code lines amount, many classes’ initialization is also omitted. And @init can help you implicitly do the asynchronous jobs like database connection initialization which is not convenient in the past.

With this ability, we can focus more on interface-oriented programming, abstraction, and have more time of code design to understand the demand and solve problems.

@provide()
@controller()
export class HomeController {

@inject()
reportService: IReportService;

@get('/')
async index(ctx) {
ctx.body = await this.reportService.getReport();
}
}

@provide()
class ReportService implements IReportService {

@inject()
reporter: IReportManager;

async getReport(id: number) {
return await this.reporter.get(id);
}
}

@provide()
class ReporterManager implements IReportManager {

@inject()
db;

@init()
initDB() {
// open connection
}

async get() {
// return data from db;
}
}

Entrance

Similar with the decorator @controller mentioned above, there is another entrances for calling. The @schedule decorator for scheduled tasks.

import { schedule } from 'midway';

@schedule({
interval: 2333, // 2.333s interval
type: 'worker', // use one worker to execute
})

export class HelloCron {
// the schedule task to executed
async exec(ctx) {
ctx.logger.info(process.pid, 'hello');
}
}

In the next version, we will release the capabilities of user custom decorators to the use of more scenes.

Framework extension

In most scenarios, the use of decorators and the injection method make business code, even the three-party modules can be well integrated.

It could be confused for the Egg.JS user that how does the original plugin, configuration, context etc is integrated into this system. Let's talk about this.

In the originally Egg system, the app and ctx object is useful that you can get everything by them. In the Midway, in order to decouple from the web layer, we hide these objects and want the business code only communicate with the IoC container.

So we provide @config and @plugin decorator to decouple, for example:

@provide()
class ReportService implements IReportService {

@config('env') // for app.config.env
env;

@plugin('httpclient') // for app.httpclient
httpclient;

@inject()
reporter: IReportManager;

async getReport(id: number) {
const rid = this.httpclient.request('/api/' + id);
return await this.reporter.get(rid);
}
}

It’s just this little adjustment that we keep the code style of the entire application consistent, no matter how the project’s maintainer switch, the maintainer guys always can quickly get started and continue to maintain the code.

At last

As we announced at the release of Pandora.js, the Midway is also a long-term maintain product by the MidwayJs team. And it will not be the last open source product. In the past few months, we plan to bring our monitoring platform named Sandbox for The community.

Finally, the Midway’s Github is https://github.com/midwayjs/midway/, belongs to the MidwayJs Group. Welcome to give us suggestions, comments or support us by click star.

references

[1] HSF is a on-premise distributed RPC protocol in Alibaba Group.

--

--