I typically write code that only I use, but I do have a public github with some JS libraries. A key component of most libraries are events. My question may be opinionated, but I don't know where else to post it and it is important to me as a perfectionist.When writing an API do events need to be re-sent through each layer, or should one expect a client to know how to access those events?Exampleclass Button {     onClick(){         this.emit('click');     }}class UI {     constructor(){           let self = this;           this.button = new Button();           // is this necessary?           this.button.on('click', function(){                self.emit('button.click');           });      }}class Application {        constructor(){             this.ui = new UI();             // this?             this.ui.on('button.click', function(){             });             // or is this acceptable?             this.ui.button.on('click', function() {              });        }}This is actually quite a broad question, as I am also asking for the necessity to do or not to do this:class Foo {    explode(){        console.log('boom');    }} class Bar {   constructor(){     this.foo = new Foo();   }   // necessary?   explodeFoo(){      return this.foo.explode();   }}class Application(){    constructor(){        this.bar = new Bar();        // this?         this.bar.explodeFoo();        // or is this fine?        this.bar.foo.explode();    }}
Do events/functions need to be redefined at each level for an API?
javascript;object oriented design
Direct callIf you want the caller to be able to manipulate the contained objects, and the main application doesn't need to do anything to coordinate such activity, then this is ok, and is less work:this.bar.foo.explode();You should bear in mind that if you ever change the implementation of bar then the caller's code will break. This is always a concern when you break encapsulation.Wrapped callIf the caller's activities need to be gated (e.g. if sometimes it is not OK to explode something) or if you need application hooks to coordinate things (e.g. to log application actions), then the following would be better:explodeFoo() {    if (this.explodeAllowed) {        this.bar.foo.explode();        this.logExplosion(Foo);    }}this.bar.explodeFoo();If you didn't have the wrapper method explodeFoo then there would be nowhere to add the explodeAllowed check or to implement the logging. For this reason, you might even want to introduce the wrapped method call before you actually need it; that way you can add hooks seamlessly in future versions.I suppose you could skip the wrapper and just write documentation telling anyone using your API that they have to check the explodeAllowed flag first, for example. However that would introduce what is known as unenforced sequential coupling, which is an anti-pattern.