dimanche 3 janvier 2021

Dependency inversion (IOC) of third party singleton

I have difficulties implementing inversion of control in order to keep loose coupling between my modules, and also preventing dependency cycle.

In my example I want to dynamically register configuration for a third party library (Apollo Client).

Current implementation

With my current implementation, I have dependency cycle issues

Folder structure

src
├── apolloClient.ts
├── modules
│   ├── messages
│   │   ├── ui.ts
│   │   └── state.ts
│   ├── users
│   │   ├── ui.ts
│   │   └── state.ts
...

apolloClient.ts

import { InMemoryCache, ApolloClient } from '@apollo/client'
import { messagesTypePolicies } from './modules/messages/state.ts'
import { usersTypePolicies } from './modules/users/state.ts'

export const client = new ApolloClient({
  cache: new InMemoryCache({
    typePolicies: {
      Query: {
        // register manually all modules
        ...messagesTypePolicies,
        ...usersTypePolicies
      }
    }
  }),
})

modules/messages/state.ts

import { client } from '../../apolloClient.ts'

export const messagesTypePolicies = {
  messagesList: {
    read() {
      return ['hello', 'yo']
    }
  }
}

export const updateMessage = () => {
  client.writeQuery(...) // Use of client, so here we have a dependency cycle issue
}

What I would like

To prevent dependency cycle and tight coupling, I would like each module to register itself, so that the apolloClient singleton has no dependency on UI modules.

modules/messages/state.ts

import { client, registerTypePolicies } from '../../apolloClient.ts'

// Use custom function to register typePolicies on UI modules
registerTypePolicies({
  messagesList: {
    read() {
      return ['hello', 'yo']
    }
  }
})

export const updateMessage = () => {
  client.writeQuery(...) // Dependency cycle issue solved :)
}

apolloClient.ts

// What I am trying to solve using IOC pattern
// Not sure how to dynamically update the singleton

Aucun commentaire:

Enregistrer un commentaire