Data from many models
I have a number of cases where I need to lookup and pass around data from many models. I see two ways of doing so in the scope of RL.
1) Inject the necessary models into a command, ask for the data necessary, and pass it in via arguments be manipulated in another model or service.
2) Use the injector to either get instances or inject directly into a composite model that can perform manipulate all the necessary data. Then call this model from a command passing whatever event data necessary.
Both would clearly dispatch their results back to the framework via an event or signal.
I would guess these are both viable, but I am a little wary, as once you start injecting models wherever you need them, it is tempting to do so in mediators/services. And once you start injecting a number of models into a command, more references and logic is performed in the command, which could affect the commands lifecycle and garbage collection
Comments are currently closed for this discussion. You can start a new one.
|?||Show this help|
|ESC||Blurs the current field|
|r||Focus the comment reply box|
|^ + ↩||Submit the comment|
You can use
Command ⌘ instead of
Control ^ on Mac
Support Staff 1 Posted by Ondina D.F. on 14 Aug, 2015 10:05 AM
Normally, commands are available for garbage collection immediately after execution. If a command doesn't get away after it has been executed, it is because something ( a long-living object or class) is keeping a reference to the command. It's your responsibility, as a developer, to avoid such situations. Do you have a concrete use case where a command is kept in memory, or are you talking theoretically? When you say that it could affect command's lifecycle, what do you mean by that?
Yes, they are viable scenarios.
Well, you should be able to resist the temptation :)
Robotlegs 2 and Swiftsuspenders make it possible to inject anything wherever you want. But, that doesn't mean you should. The MVCS meta-design pattern gives you some guidance on the roles of the tiers and on how to design your classes. You know already that you can either follow the recommended practices stricto modo, or that you can adapt them to your needs. Or, that you can use another pattern.
Regarding the amount of logic you put into a command, you are right if you think that a command shouldn't become bloated with logic. Maybe you just need to split it into several commands, each being responsible for just a part of the business logic. Macrobot is a utility/extension that provides two ways (sequence, parallel) to batch commands.
2 Posted by dkarten on 14 Aug, 2015 02:25 PM
Occasionally an async action is performed by a command, which is why I am worried about proper garbage collection. What is exactly is the disposal process for a command?
I know that if you give an asynchronous service process a callback that resides in a command, this will maintain a reference to it. What about local variables created in a commands execute method? Sometimes I find myself performing a few manipulations on a command, or in a command, taking data from one model, manipulating it in another model, and then running it off to a service. I am guessing this is a sign it needs to be split up into multiple commands, because we start violating the SRP. I will look into macrobot.
3 Posted by dkarten on 14 Aug, 2015 03:53 PM
The use case is looking up data imported from an external file. Data from the file needs to be matched/merged with data kept in several models. Occasionally, if the data cannot be found in memory (aka the models), a database lookup needs to be performed. My current solution is a sort of aggregate model (or perhaps its more of a service?) that has injections for the necessary models and the db service (this is dirty I know but for right now I cannot see a way to separate this service dependency). This aggregate model takes the parsed file data and manipulates it, querying the other models when necessary. While I am pretty happy with it, the data is short lived and not stateful, which makes me think it should be processed in a command. I guess the aggregate model can be instantiated on the fly with the injector?
Support Staff 4 Posted by Ondina D.F. on 14 Aug, 2015 04:10 PM
Inside of CommandExecutor.executeCommand() the command object has a local scope.
The command is instantiated and its execute method is called.
The "execute" method to invoke on the Command instance is set to the default 'execute'. (Btw., You can set it to another method of your choice, when you map the event to the command).
If there is none keeping a reference to that locally instantiated command, it will get gc-ed:
Exactly. But, only until the async process is complete. After that the command goes away.
This is actually what we expect to happen when working with async calls in a command.
What do you mean?
Sometimes it makes sense to have a group of actions inside a single command, if those actions are somehow related. Theoretically, I'd prefer to have a single command accessing 2 models and a service instead of 4 commands doing the same job. But, as always, there are many factors influencing such a decision in a concrete use case. You'll have to find out for yourself how granular you want to go.
Here the macrobot that works with robotlegs 2 :
Support Staff 5 Posted by Ondina D.F. on 15 Aug, 2015 12:50 PM
I didn't see your last message when I responded yesterday.
YES!! This is much better than using the hybrid class you've mentioned.
The loading of data from an external file would be a job for a Service class-FileSystemService
DataComparator could be a class that compares the data from the file with existing models' data.
That'd be a job of a DBService.
File System Service------> DataComparator Model<------DB Service
A command could orchestrate the entire process, if it had the 2 Services and the DataComparator class injected into it.
Or, if you wanted to use a sequence of commands (macrobot), you could use a command to encapsulate the loading of the file data , another command to perform the data comparison, and a third command to make a db request. 3 tasks, 3 commands.
6 Posted by dkarten on 19 Aug, 2015 09:06 PM
Macrobot is great, thank you for recommending it. That is exactly what I've done. I made an IFileReaderService and implemented it with a DelimitedFileService (it happens that all files that we are reading are delimited with commas, semicolons, etc). The DelimitedFileService simply reads the delimited data and returns it in an array of row arrays. This data gets passed to a model which parses the raw data into a more logical form (for now just Objects, but could be typed later). Our parsed data is passed to a command which calls the DataComparator model, performs its model lookups, performs whatever db lookups necessary, and returns the matched data.
I know that it is breaking principles to combine the DataComparator model with the db service, but I need to keep the data being matched together. For now I have not figured out a way to do so, but I've thought of maybe storing the data being matched in the DataComparator model, returning a list of conflicts that need to be looked up in the database, and then returning to the DataComparator model with db data, but I've left out doing this for now, I have other things more important to work on.
If you have any suggestions, great, but if this seems good enough, you can close this!
Support Staff 7 Posted by Ondina D.F. on 20 Aug, 2015 10:48 AM
Yes, that is the better way of doing it. The Model shouldn't have to care about how or from where the data is received. You could go a step further and encapsulate the logic for data comparison in another class, especially if you think that this kind of data comparison will be needed in more places. The Model would call methods on the comparator class (injected into the Model), and would just keep the data in memory as long as needed and/or prepare it for further use.
Instead of calling a Service directly from a Model:
the Model can dispatch events to trigger Command(s) that would call a Service
a Command, having the Model injected, can take the result from Model's operations on the data and pass it to a Service, also injected into the Command
The result from a Service call can be received in a call-back function inside a Command, or the Service dispatches events to trigger Commands.
When using Macrobot, commands can be executed directly from within your batch command. This might come in handy every now and then, so I thought I mention it.
Also, Direct Command Map is a robotlegs extension that does a similar job as the Macrobot:
Right. Go with what works for now, and since you are aware of the deficiencies in the code/design, I'm sure you'll come back to it later to improve and refine it.
Ondina D.F. closed this discussion on 20 Aug, 2015 10:48 AM.
dkarten re-opened this discussion on 02 Sep, 2015 03:27 PM
8 Posted by dkarten on 02 Sep, 2015 03:29 PM
Another question about centralizing data into one model. I am finding I have a bunch of models that are basically lists. The only functionality I have added to them is to add and remove items from the list they manage. This got me thinking perhaps it would be better to write them as list classes, and have one model that deals with all the lists. I constantly find myself needing to reference more than one list in a single method or procedure.
Is it poor practice to have one model manage multiple lists?
Support Staff 9 Posted by Ondina D.F. on 02 Sep, 2015 04:56 PM
It depends on the number of lists and on their roles, or on the role of that model or on the type or level of its cohesion.
Having a single Model managing all lists of an application is going to be a problem in case the application grows in size and complexity over time and you have a lot of logic inside of this global Model or if tight coupling is one of the side effects of this approach.
But, maybe in the context of your application it makes sense to centralize the adding and removing of list items like you said. I don't know. You have to know this:)
dkarten closed this discussion on 08 Sep, 2015 06:28 PM.