Skip to content
This repository has been archived by the owner on May 21, 2024. It is now read-only.

Standartize transport and client interfaces #55

Closed
Tracked by #49
denis-ilchishin opened this issue Dec 10, 2023 · 0 comments · Fixed by #57
Closed
Tracked by #49

Standartize transport and client interfaces #55

denis-ilchishin opened this issue Dec 10, 2023 · 0 comments · Fixed by #57
Assignees

Comments

@denis-ilchishin
Copy link
Owner

denis-ilchishin commented Dec 10, 2023

It would be great to come up with common contracts for adapters and their clients.
Here's common logic, that relates to all transports:

  1. Client make calls to the server via RPC.
  2. Server handle client's RPCs by declareProcedure handlers and replies with the result.

This is nice workflow, because it provides clear and easy way for client and server to communicate in request-response manner. But in this case only client initiate a communication, which is not suitable for "real-time" applications. So, there's definitely a necessity for a server to be able to send some events to a client.

declareProcedure({
  handle: ({ client }) => {
     client.send('some server event')
     return 'RPC reply'
  },
})

with something like client interface it would be possible to isolate underlying transport implementation, so application code does not rely on actual transport. The only thing it should know, is that there's a client, and it possible to send some event.

But that comes with the problem, that some transports could be used for bi-directional communication, but some couldn't. HTTP does not support bi-directional communication, so server won't be able to send anything, except a response to that particular call.

Proposed interface for server-side Client:

interface Client <CustomData extends any> {
  id: string // uuid
  send: (eventName: string, payload: any) => boolean // whether is successfully sent
  data: CustomData
}
  • id - client's unique identifier. E.g, for HTTP it would be id of a particular request; for websockets, it will be id of a particular websocket connection; etc..
  • send - method to send event to the client by server. Responds with boolean to indicate whether that event was successfully sent
  • data - application-related data to assign/attach to a client. It could be anything, like some auth info, application user's data, etc

Example for HTTP client provider:

// this is the only provider that application will have to adjust if the transport has changed
// so the rest of the application just end up working flawlessly
const clientDataProvider = declareClientDataProvider((ctx, transportData: any) => {
  const { cookie, authorization } = transportData.headers
  const userToken = getUserTokenFromCookie(cookie);
  // something like machine-to-machine communication, if you app is also supports not just only real users, but also other type of clients, e.g other services
  const m2mToken = getM2MTokenFromHeader(authorization)
  return { userToke, m2mToken }
})

transportData - some data from underlying transport, so application could handle transport specific things if needed, e.g cookie for HTTP transport. This is the only thing, that could not be isolated, and application will have to handle it properly by itself.

Then it could be implemented like the following:

// for procedures that work with actual real users
const userProvider = declareProvider( async ({ client, injections }) => {
  const { db } = injections
  const { userToken } = client.data
  if(!userToken) return null
  return await db.getUserByToken(token)  
}, {
 // like some db connection injection...
})
// for procedures that communicate with other services
const clientProvider = declareProvider(({ client }) => {
  const { m2mToken } = client.data
  return getM2MClient(m2mToken)
})

In the end this will allow to create application with both monolith and microservices architectures, that are able communicate with each other, as well as with real users. And at the same time be completely transport-agnostic, so it can be swapped without any impact on the application code and businness logic.

@denis-ilchishin denis-ilchishin mentioned this issue Dec 10, 2023
31 tasks
@denis-ilchishin denis-ilchishin changed the title standartize client/adapters common contracts for concistency Standartize client/adapters Dec 10, 2023
@denis-ilchishin denis-ilchishin self-assigned this Dec 10, 2023
@denis-ilchishin denis-ilchishin changed the title Standartize client/adapters Standartize transport and client interfaces Dec 10, 2023
@denis-ilchishin denis-ilchishin linked a pull request Dec 12, 2023 that will close this issue
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant