Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CodeFest 2018. James Akwuh (Adform) — DI: DIY

CodeFest 2018. James Akwuh (Adform) — DI: DIY

Посмотрите выступление James: https://2018.codefest.ru/lecture/1272/

В сообществе JS редко обсуждают паттерн Dependency Injection, хотя он является одним из самых важных блоков в построении архитектуры большого приложения. Я всегда восхищался реализацией DI в Java-фреймворке Spring, и последние два года неоднократно пытался реализовать аналогичный подход. Наконец, получилось.

В докладе мы рассмотрим следующие вещи:
— Что такое DI и какое место он занимает в архитектуре приложения?
— Как правильно использовать DI?
— Насколько простым может быть DI в JS?
— Чего можно добиться с помощью Decorators Metadata в TS?— Примеры.

CodeFest

April 05, 2018
Tweet

More Decks by CodeFest

Other Decks in Programming

Transcript

  1. class OfferController { showOffersPage() { fetchOffer(id).then(offer => { const offerView

    = new OfferView({ model: offer, user: this.appUser }) this.layout.getRegion('main').show(offerView) }); } // ... } 4
  2. class OfferComponent extends React.Component { componentDidMount() { const {dispatch, query:

    {offerId}} = this.props dispatch(fetchOffer(offerId)) } // ... } 5
  3. class OfferComponent extends React.Component { static async getInitialProps({ req })

    { return fetchOffer(req.query.id).then(offer => { return {offer} }) } // ... } 6
  4. 7

  5. static async getInitialProps({ req }) { const offerPromise = fetchOffer(req.query.id)

    const programPromise = fetchUser() .then(user => fetchReferralProgram(user)) return Promise.all([offerPromise, programPromise]) .then(([offer, program]) => { return {offer, program} }) } 9
  6. static async getInitialProps({ req }) { const user = await

    fetchUser() const offerPromise = fetchOffer(req.query.id, user) const programPromise = fetchReferralProgram(user) return Promise.all([offerPromise, programPromise]) .then(([offer, program]) => { return {offer, program} }) } 10
  7. 11

  8. 15

  9. IoC IoC is for is for Vendetta Vendetta Vendetta Vendetta

    Inversion of Control Inversion of Control 21
  10. class OfferComponent extends React.Component { static async getInitialProps({ req })

    { return fetchOffer(req.query.id).then(offer => { return {offer} }) } // ... } 22
  11. import Database from 'interfaces' import DatabaseSymbol from 'symbols' locator.set(MongoDB, MongoDB.factory)

    locator.set(Database, MongoDB.factory) locator.set('database', MongoDB.factory) locator.set(DatabaseSymbol, MongoDB.factory) 25
  12. class ServiceLocator { factories = new Map() // Usage: locator.set(Database,

    MongoDB.factory) set(token, factory) { this.factories.set(token, factory) } // Usage: locator.get(Database) get(token) { return this.factories.get(token)(this) } } 27
  13. class UserRepository { constructor(locator) { this.locator = locator } findAll()

    { if (!this.database) { this.database = this.locator.get(Database) } // ... } 29
  14. // UserRepository.spec.js locator.set(Database, () => mockDatabase) const repository = locator.get(UserRepository)

    repository.findAll().then(() => { expect(mockDatabase.lastQuery) .toEqual('select * from ...') }) 33
  15. ✅ Плюсы ✅ Плюсы Помогает соблюдать закон Деметры Обеспечивает полную

    модульность Делает модуль самодокументируемым 37
  16. @Component public class Company { private Employee employee; @Autowired public

    Company(Employee employee) { this.employee = employee; } } 43
  17. export class ZoneCreateParams { @Length(1, 250) name: string } export

    class ZoneController { @Inject() public zoneRepository: ZoneRepository; @Post('/zones') @Authorized() async create(@Body() body: ZoneCreateParams, @CurrentUser({required: true}) currentUser: User) return this.zoneRepository.createZone(body, currentUser) } 51
  18. @Component public class Company { private Employee employee; @Autowired public

    Company(Employee employee) { this.employee = employee; } } 53
  19. class UserRepository { @Decorate() method(database: Database) { } } __decorate([

    Decorate(), Reflect.metadata("design:type", Function), Reflect.metadata("design:paramtypes", [Database]), Reflect.metadata("design:returntype", void 0) ], UserRepository.prototype, "method"); 59
  20. // container.get(UserRepository) const neededTokens = Reflect.getMetadata('design:paramtypes', UserRepository) const args =

    neededTokens.map(token => container.get(token)) return new UserRepository(...args) 60
  21. // container.get(UserRepository) const neededTokens = Reflect.getMetadata('design:paramtypes', UserRepository) const args =

    await Promise.all( neededTokens.map(token => container.get(token))) return new UserRepository(...args) 62
  22. class Props { @Inject() currentUser: CurrentUser; @Inject(() => AbstractRouter) router:

    AbstractRouter; @Inject() zones: CurrentZones; } export class HomeContainer extends React.Component<Props> { constructor(props: Props) { // ... 66