Created
September 27, 2012 18:17
-
-
Save mbriggs/3795517 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Currently (as I understand it), BBCloneMail is organized as a hierarchy of modules, | |
| * where all modules communicate via req/resp, or commands, and all internals are implementation | |
| * details. | |
| * | |
| * for the purposes of conversation, I am calling modules that are conceptually bodies of code as | |
| * "sub applications", and modules that interact in those bodies of code as "modules" | |
| */ | |
| // psudo-code | |
| module('contacts', function(){ | |
| app.respondTo("contact:find-all", controller.findAll, controller) | |
| }) | |
| /* | |
| * This is not a problem for smaller things. But the level of business logic for the thing I am | |
| * about to build is non trivial, and I don't think it would scale. | |
| * | |
| * Lets say you are in a "family" of modules. If you have one module playing the role of "repository", | |
| * requests like "contacts:find-all" are fine. But lets say you have 5 repositories, and each | |
| * one is implementing 10 methods, and you also have more use-case style controllers for the same | |
| */ | |
| module('contacts-repository', function(){ | |
| app.respondTo("contact:find-all", controller.findAll, controller) | |
| }) | |
| // controllers are fine for simple-ish things, but when you have more complex coordinations and | |
| // business logic, it can be nice to break out module services that model a use case | |
| module('contacts-deleting', function(){ | |
| controller = { | |
| delete: function(contact){ | |
| var condition = app.request("some-module:condition-met?") | |
| $.when(condition).then(function(){ | |
| if(contact.foo && condition){ | |
| app.execute('contact:destroy', contact) | |
| } else { | |
| app.execute('notify:error', "Some message") | |
| } | |
| }) | |
| } | |
| } | |
| app.respondTo("contact:delete", controller.delete, controller) | |
| }) | |
| /* | |
| * obviously, the above example isn't even close to as complex as these thing can easily get. Also, | |
| * maybe you would want to say that each request should have the actual module name that receives it | |
| * as prefix (although this breaks the abstraction a bit). But by flattening all module "methods" into | |
| * a single namespace, I think you are losing a lot of clarity into what is actually happening, and | |
| * trading it for a high level of decoupling. | |
| * | |
| * In simple cases this is probably preferrable, but let's say you have 30 or 40 different interactions | |
| * that are all passing through the sub app as an event bus. I can see that tradeoff no longer being | |
| * worth it. | |
| * | |
| * I think a more traditional service locator approach would solve this problem. So would DI, but | |
| * I think service location fits better with marionette modules | |
| */ | |
| // quick and dirty service locator implementation | |
| ;(function(Marionette){ | |
| var services = { | |
| store: {}, | |
| get: function(name){ return this.store[name] }, | |
| set: function(name, value){ this.store[name] = value } | |
| } | |
| _.bindAll(services) | |
| Marionette.Application.prototype.service = services.set | |
| Marionette.Application.prototype.service = services.get | |
| }(Backbone.Marionette)); | |
| // in each of the different module files | |
| app.service("contact-repository", ContactRepository) | |
| app.service("notifier", Notifier) | |
| app.service("some-module", Something) | |
| // use case | |
| module('contacts-deleting', function(){ | |
| // ask the sub app to find the services this current service needs to do its job | |
| var repository = app.locate('contact-repository'), | |
| notifier = app.locate('notifier'), | |
| something = app.locate('something') | |
| controller = { | |
| delete: function(contact){ | |
| var condition = something.isConditionMet(contact) | |
| $.when(condition).then(function(){ | |
| if(contact.foo && condition){ | |
| repository.destroy(contact) | |
| } else { | |
| notifier.error('Some Message') | |
| } | |
| }) | |
| } | |
| } | |
| app.service('DeletingContacts', controller) | |
| }) | |
| /* | |
| * I think the benefits are | |
| * | |
| * 1 - we are back more into a "interactions with other objects" world, rather then a "everything is like a private method world". This can make it easier to understand what role a given thing plays, just by looking at what its dependancies are. | |
| * 2 - it can make it easier to find out who is using a given thing -- you only have to grep for who is asking for it, not every message it responds to | |
| * 3 - it exposes a bit more of the structure of the dependancies, which can give you hints on where to go next when tracing the code in complex scenarios | |
| * | |
| * downsides | |
| * | |
| * 1 - it is a bit less decoupled (although still pretty damn decoupled), and you stop thinking about interactions within a sub app as a flat thing. | |
| * | |
| * 2- tests aren't quite as nice, you need to now pass in stubs rather then just test messaging. i think this could be a good thing too due to clarity, but it is definately more typing | |
| */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Currently (as I understand it), BBCloneMail is organized as a hierarchy of modules, | |
| * where all modules communicate via req/resp, or commands, and all internals are implementation | |
| * details. | |
| * | |
| * for the purposes of conversation, I am calling modules that are conceptually bodies of code as | |
| * "sub applications", and modules that interact in those bodies of code as "modules" | |
| */ | |
| // psudo-code | |
| module('contacts', function(){ | |
| app.respondTo("contact:find-all", controller.findAll, controller) | |
| }) | |
| /* | |
| * This is not a problem for smaller things. But the level of business logic for the thing I am | |
| * about to build is non trivial, and I don't think it would scale. | |
| * | |
| * Lets say you are in a "family" of modules. If you have one module playing the role of "repository", | |
| * requests like "contacts:find-all" are fine. But lets say you have 5 repositories, and each | |
| * one is implementing 10 methods, and you also have more use-case style controllers for the same | |
| */ | |
| module('contacts-repository', function(){ | |
| app.respondTo("contact:find-all", controller.findAll, controller) | |
| }) | |
| // controllers are fine for simple-ish things, but when you have more complex coordinations and | |
| // business logic, it can be nice to break out module services that model a use case | |
| module('contacts-deleting', function(){ | |
| controller = { | |
| delete: function(contact){ | |
| var condition = app.request("some-module:condition-met?") | |
| $.when(condition).then(function(){ | |
| if(contact.foo && condition){ | |
| app.execute('contact:destroy', contact) | |
| } else { | |
| app.execute('notify:error', "Some message") | |
| } | |
| }) | |
| } | |
| } | |
| app.respondTo("contact:delete", controller.delete, controller) | |
| }) | |
| /* | |
| * obviously, the above example isn't even close to as complex as these thing can easily get. Also, | |
| * maybe you would want to say that each request should have the actual module name that receives it | |
| * as prefix (although this breaks the abstraction a bit). But by flattening all module "methods" into | |
| * a single namespace, I think you are losing a lot of clarity into what is actually happening, and | |
| * trading it for a high level of decoupling. | |
| * | |
| * In simple cases this is probably preferrable, but let's say you have 30 or 40 different interactions | |
| * that are all passing through the sub app as an event bus. I can see that tradeoff no longer being | |
| * worth it. | |
| * | |
| * I think a more traditional service locator approach would solve this problem. So would DI, but | |
| * I think service location fits better with marionette modules | |
| */ | |
| // quick and dirty service locator implementation | |
| ;(function(Marionette){ | |
| var services = { | |
| store: {}, | |
| get: function(name){ return this.store[name] }, | |
| set: function(name, value){ this.store[name] = value } | |
| } | |
| _.bindAll(services) | |
| Marionette.Application.prototype.service = services.set | |
| Marionette.Application.prototype.service = services.get | |
| }(Backbone.Marionette)); | |
| // in each of the different module files | |
| app.service("contact-repository", ContactRepository) | |
| app.service("notifier", Notifier) | |
| app.service("some-module", Something) | |
| // use case | |
| module('contacts-deleting', function(){ | |
| // ask the sub app to find the services this current service needs to do its job | |
| var repository = app.locate('contact-repository'), | |
| notifier = app.locate('notifier'), | |
| something = app.locate('something') | |
| controller = { | |
| delete: function(contact){ | |
| var condition = something.isConditionMet(contact) | |
| $.when(condition).then(function(){ | |
| if(contact.foo && condition){ | |
| repository.destroy(contact) | |
| } else { | |
| notifier.error('Some Message') | |
| } | |
| }) | |
| } | |
| } | |
| app.service('DeletingContacts', controller) | |
| }) | |
| /* | |
| * I think the benefits are | |
| * | |
| * 1 - we are back more into a "interactions with other objects" world, rather then a "everything is like a private method world". This can make it easier to understand what role a given thing plays, just by looking at what its dependancies are. | |
| * 2 - it can make it easier to find out who is using a given thing -- you only have to grep for who is asking for it, not every message it responds to | |
| * 3 - it exposes a bit more of the structure of the dependancies, which can give you hints on where to go next when tracing the code in complex scenarios | |
| * | |
| * downsides | |
| * | |
| * 1 - it is a bit less decoupled (although still pretty damn decoupled), and you stop thinking about interactions within a sub app as a flat thing. | |
| * | |
| * 2- tests aren't quite as nice, you need to now pass in stubs rather then just test messaging. i think this could be a good thing too due to clarity, but it is definately more typing | |
| */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment