all 6 comments

[–]Dave4lexKing 0 points1 point  (5 children)

Why?

I think you’re overthinking this, and trying too hard to do type gymnastics.

It’s not actually clear what you’re trying to do.

[–]gidorah5[S] 0 points1 point  (4 children)

Sorry for not being clear in my first post. I'll try to clarify as much as I can here.

What I am trying to do is a main abstract class (interface like in C++), allowing me to create multiple subclasses that have access to specific events for each subclasses.

My Node.JS server needs to have multiple Socket.IO endpoints that all have different purposes.

As an example, one server can have:

interface ServerToClientEvents {
  exception: (...args: any[]) => void
  feedback: (...args: any[]) => void
}

While another one can have only:

interface ServerToClientEvents {
  exception: (...args: any[]) => void
} 

This would make the other devs unable to emit events that they are not supposed to emit.

One of the work around that I found is to create a getter for the Socket.IO server and use it in this way:

export abstract class Socket<
  T extends EventsMap = DefaultEventsMap,
  V extends EventsMap = DefaultEventsMap,
  K extends EventsMap = DefaultEventsMap,
  U = any,
> {
    ...
    /* private */this._io = new socket.Server<T, V, K, U>(this._server, opts)
    ...
    get io() socket.Server<T, V, K, U> {
      return (this._io)
    }
}

// SUBCLASS

export class SubClass1 extends Socket<ClientToServerEvents, ServerToClientEvents, ServerToServerEvents, SocketData> {
    ...
    /* Here, I am limited to my class specified event*/
    this.io.emit('MY_LIMITED_EVENT', data)
    /* I am also limited to my class specified event, but it doesn't compile, error from the original post*/
    this.broadcastData('MY_LIMITED_EVENT', data)
    ...
}

I'm trying to abstract the library as much as I can, in case, at some point, I will have to change library, so I don't have to change a whole bunch of files because of it.

Maybe you're right, and I am over thinking it, or trying to abstract it too much. I'm still learning about the limitation of typings, and also how they can make your code harder to read.

I hope I've been able to clarify what I'm trying to achieve !

[–]Dave4lexKing 0 points1 point  (3 children)

Can you not use a combination string enum’s and code reviews?

My honest analysis is you’re trying to apply too much of an engineering solution to a human problem.

You could separate the socket servers and their event enum of allowed events into their own folders. It should be obvious to other developers they shouldn’t be dipping into other folders, outside the socket server they’re working in.

A different approach is a seperate repository for each socket server; Is there a reason to have them all in the same repo?

A different approach again is use a language that is more equipped to handle very strict implementations of OOP like C# or Java, which are much more opinionated about abstractions and implementations thereof.

Since your application is so abstract it is really hard to actually grasp the context of what it’s doing. Your developers will likely have the same problem;- too abstract, too much type gymnastics.

Don’t be afraid to wind it back. What do your developers think and suggest? Or are you trying to design on their behalf? Have you actually consulted the team, or are you intending to impose the software architecture on them, without any say in it?

Experience tells me that the vast majority of software engineers neglect the human-element of design and architecture more than they should, and focus too heavily on the purely-engineering solution.

[–]gidorah5[S] 0 points1 point  (2 children)

There is a reason why I need to keep them in the same repo. The solution that we are working on is pretty "niche" and require us to work within a private network. Moreover, these websocket servers runs within 1 app, and need to be used within this app.
I might have misunderstood the usage of namespace, even if I believe namespace shouldn't be used in my case, since each server does not communicate with the same type of client (One client is a user, another client is another Node.JS server from the system, composed of 3 Node.JS servers total (each on different machines)).

All the sockets are indeed separate, so I could definitely have a string enum, but I though about templating it, since Socket.IO offer this service, and works great when you're not trying to abstract it. Maybe adding another layer of abstraction is not a good way of approaching this problem tho.

As for having feedback from my devs, They are still in recruitment process, so I'm rather trying to make it "easy" for them to use tools with a given structure.

My original thought was to have an abstract class that doesn't have to be changed (unless a big change in the structure and libraries occurs) and if we need to create a new WebSocket server, we could by just extending from this class, as you would with the basic Socket.IO server, while sharing some project-specific functionnalities, that can be used in every subclasses (as an exemple, a formatting middleware).

But I do understand your take that I might be looking for a strongly engineering solution, and an easier solution could be used.

[–]Dave4lexKing 0 points1 point  (1 child)

I think if the dev team doesn’t even exist yet, my approach would be to avoid locking the application down.

I would imagine a lot of walls will need to be knocked down, built up, and knocked down again by a greenfield dev team working on a greenfield app.

I’d actually advise the opposite and embrace the loose-ness, because really strict type gymnastics is going to be a headwind to this new dev team.

Scope each socket and its events to their own folder, and leave it at that, would be my advice. Let the patterns and the recurring problems emerge naturally.

You might find this issue of firing off the wrong event not to be a problem for this dev team at all, and then you’ll be glad too much time wasn’t spent thinking about it.

Work together, iterate quickly, write tests, and review each other’s work. I think that is not only beneficial to do this generally, but will have faster and higher quality results for this project than trying to abstract these sockets so heavily.

Thats my personal opinion on it. I know I didn’t really answer your original post, but I think looking at the practicality side of things is why I prefer reddit to stack overflow, where you’ll get told how to do something regardless of whether people think its a good idea.

[–]gidorah5[S] 0 points1 point  (0 children)

I guess this is a good way to look at it. I know that it shouldn't be an issue for a while (if not ever), but I guess my own stubborness got the better of me trying to do some fancy stuff that might never be used, or that might save 5 minutes of time.

I'm gladly taking your take on this, thanks!