[Kites.js] #3 DI System: Injectable, Inject, Providers và Services


  • Global Moderator

    Kites.js là một Framework có khả năng tự thân lắp ráp các phần mở rộng extensions để tạo thành một ứng dụng cụ thể.

    Với những dự án lớn, code nhiều, DI là thứ rất cần thiết để đảm bảo code dễ bảo trì, dễ nâng cấp. Do đó, Kites.js trang bị một DI System để làm cho việc sử dụng và khởi tạo các sự phụ thuộc một cách đơn giản.

    Bài này cố gắng giải thích DI System là gì và được sử dụng trong Kites.js như thế nào.

    Full mã nguồn ví dụ, bạn có thể tải về tại đây:

    Dependency Injection

    Cho những bạn mới chưa biết, thì DI là viết tắt của Dependency Injection.

    Theo Wikipedia, và mình dịch lại đại ý như thế này:

    Dependency injection là một kỹ thuật cho phép một đối tượng có thể cung cấp các phụ thuộc của một đối tượng khác. Một phụ thuộc là một đối tượng có giá trị sử dụng (ví dụ: nó có thể là một hằng số hoặc một Service nào đó, chẳng hạn TodoService).

    Sự phụ thuộc là một quan hệ phụ thuộc nhau, như hình dưới đây thì A đang phụ thuộc vào B, lý do A cần sử dụng một vài phương thức của B.

    84c174e5-2c19-4fb8-b14b-333a47d5a065-image.png

    Như vậy, phụ thuộc có nghĩa trông cậy vào một sự hỗ trợ, trợ giúp nào đó. Việc chuyển nhiệm vụ khởi tạo các phụ thuộc cho đơn vị khác và trực tiếp sử dụng biến phụ thuộc đó được gọi là dependency injection.

    Ở bài số #2, trong đoạn mã nguồn định nghĩa TodoController, thì controller này đang phụ thuộc vào TodoService. Bạn có thể thấy TodoController không quan tâm đến TodoService được khởi tạo như thế nào cả, đúng ko? Nó chỉ quan tâm đến việc đón nhận các Requests và sử dụng trực tiếp các methods của svTodo.

    import { Controller, Get, Put, RequestParam, Delete, Post, RequestBody } from '@kites/rest';
    import { TodoService } from './todo.service';
    
    @Controller('/todo')
    export class TodoController {
    
      constructor(public svTodo: TodoService) { }
    
      @Get('/')
      list() {
        return this.svTodo.getAll();
      }
    
      @Get('/:id')
      details(@RequestParam('id') task) {
        return this.svTodo.get(task);
      }
    
      @Post('/')
      create(@RequestBody() body) {
        return this.svTodo.create(body);
      }
      // ....
    }
    

    Kites Container, Kỹ thuật Inversion of Control (IoC)

    Bên cạnh 4 tính chất của lập trình hướng đối tượng OOP thì IoC là mẫu thiết kế tuân thủ nguyên lý số 5 Dependency Inversion trong nguyên lý thiết kế phần mềm hướng đối tượng S.O.L.I.D. Tức là, các Module cấp thấp sẽ được inject (truyền vào) vào Module cấp cao thông qua Constructor hoặc thông qua Properties.

    Như ví dụ ở trên thì svTodo được truyền vào Constructor, taskbody được truyền vào dưới dạng tham số của methods.

    Như vậy, nếu bạn để ý thì TodoController đã ủy nhiệm cho một đơn vị chịu trách nhiệm khởi tạo TodoService, đó là DI Container hay còn gọi là IoC Container.

    Kites.js cài đặt một DI Container trong core engine, có nhiệm vụ là nạp các phụ thuộc trong quá trình khởi động, quản lý và khởi tạo các sự phụ thuộc khi có yêu cầu.

    Tuy nhiên, trong phiên bản hiện tại v1.1.2, thì chúng ta phải config auto-mát-tay các Service phụ thuộc trong file ./app.ts, bằng cách định danh các providers như trong đoạn mã dưới đây:

    import { TodoService } from './todo/todo.service';
    
    async function bootstrap() {
      const app = await KitesFactory
        .create({
          providers: [
            TodoService,
          ],
        })
        .use(Express)
        .use(Rest)
        .init();
    
      app.logger.info(`Server started! Let's browse http://localhost:3000/api/todo`);
    }
    

    Sau khi KitesInstance được tạo, bạn có thể lấy được instance của TodoService, bằng cách gọi lệnh:

    const svTodo = app.container.inject(TodoService);
    const tasks = svTodo.getAll();
    console.log(tasks);
    

    @Injectable() và @Inject()

    TODO: update post ... chờ cập nhật thêm nhé bạn!