I would agree with the parent here, try to not go into abstractions until they are apparent but there are some pretty solid patterns that have worked thru the years and still hold up today.
The first being pub-sub, this can be an event system or and observer pattern but it is good to have loose coupling of when something happens it can loosely tell it out into the ether and not care about if anyone is listening.
The second being sole responsibility of change, this does not have to be something as formal as a Redux or some other library if you are using a back-end language but there should be one function that changes one segment of data and that is the one function that owns the writing of that data.
The third is more optional but in certain places it really shines and has become kind of a lost art and that is plug-in style interfaces. Say you have a portion of you app that parses text files and stores them in a common data structure and you know that there will be future text file formats but you do not know what they will be. A plugin architecture is a natural fit here, where you define the interface and the data storage side, then you only need to implement a plugin for the parser side for each new file format. I always write these as true plugins where you can drop a new one in a plugin folder and the application picks it up, no recompiling no configuration, no restarting, just a black box that is self contained.
I fully agree with the first. You usually need some designed way of handling communication and/or IO in a sufficiently large application. Whether these are event queues, channels, commands, pipelines or w/e depends. But thinking about this up-front is the key here.
I would describe the second as a constraint rather than an abstraction. And I fully agree with this. The most obvious and probably most talked about benefit of FP would be avoiding state management complexities.
The third one is neat. But I would say we're already in danger territory here. Just writing a function and naming it properly is the minimal step required to make refactoring into a strategy or plugin pattern later fairly straightforward.
The first being pub-sub, this can be an event system or and observer pattern but it is good to have loose coupling of when something happens it can loosely tell it out into the ether and not care about if anyone is listening.
The second being sole responsibility of change, this does not have to be something as formal as a Redux or some other library if you are using a back-end language but there should be one function that changes one segment of data and that is the one function that owns the writing of that data.
The third is more optional but in certain places it really shines and has become kind of a lost art and that is plug-in style interfaces. Say you have a portion of you app that parses text files and stores them in a common data structure and you know that there will be future text file formats but you do not know what they will be. A plugin architecture is a natural fit here, where you define the interface and the data storage side, then you only need to implement a plugin for the parser side for each new file format. I always write these as true plugins where you can drop a new one in a plugin folder and the application picks it up, no recompiling no configuration, no restarting, just a black box that is self contained.