|
I'm coming from a .NET background, where it is a practice to not bind domain/entity models directly to the view in not-so-basic CRUD-ish applications where the view does not directly project entity fields as-is. I'm wondering what's the practice in RoR, where the default persistence mechanism is ActiveRecord. I would assert that presentation-related info should not be leaked to the entities, not sure though if this is how real RoR heads would do it. If DTOs/model per view is the approach, how will you do it in Rails? Your thoughts? Some examples: - A view shows a list of invoices, with the number of unique items in one column. - A view shows a list of credit card accounts, where possibly fraudulent transactions are executed. For that, the UI needs to show accounts that have more than one transaction in red. For both scenarios, The lists don't show all of the fields of the entities, just a few to show in the list (like invoice #, transaction date, name of the account, the amount of the transaction) For the invoice example, The invoice entity doesn't have a field "No. of line items" mapped on it. The database has not been denormalized for perf reasons and it will be computed during query time using aggregate functions. For the credit card accounts example, surely the card transaction entity doesn't have a "Show-in-red" or "IsFraudulent" invariant. Yes it may be a business rule, but for this example, that is a presentation concern, so I would like to keep it out of my domain model. EDIT: A new example:
BusListDTO can be used to show information to the UI, or serialize it as JSON/XML should the need arises. Without a DTO, the entity/AR will have a lot of members that are not really persisted since the entity evaluates them for every call. Some examples:
Should the Bus entity be involved in a lot of views that does not directly show its shape, you end up with a monolithic AR class. In a large team scenario, this is a maintenance nightmare, where you spend a lot of time communicating what attributes do in specific use-cases and you have devs who use attributes indiscriminately. |
|
Flattened or unattached ActiveRecord models are a bad choice for DTO's. As the name implies, AR objects are meant to be persistence objects, bundled with ALL the functionality to handle data persistence - dynamic finders, validations, observers, callbacks, as in everything - which I'm pretty sure you won't be needing for presentation purposes alone. What you maybe looking for is a mix of simple Ruby classes and ActiveModel. You can transform any Ruby class to act like ActiveRecord, without having to load all of its functionality and avoid the unnecessary bloat.
Need serialization? Include ActiveModel::Serialization in your class:
You can then implement your own "build_transfer_object" method inside your AR model:
|
|
Been pondering on this and here are some of my notes...
|
|
Everything in RoR is a class, in essence, objects. Hence data transfer objects aren't explicitly needed. Want a field called line_item for the invoices to represent the number of transactions mapped to it? Declare an instance method in the invoice model which may be then be called anywhere you can instantiate an invoice. You can also go ahead and declare an :invalid? method on the invoice model that will simply return true if the invoice_count is greater than 1. Want to dynamically render different HTML or CSS in relation to your invoice? Use a helper (resource module accessible from the views) method which will read the value of the :invalid method you just made, then rendering the "Red" CSS class should the current invoice being rendered is indeed, invalid. Based solely on you example, and assuming all Models, Views and Controllers have been set up, rails can render the invoice view with something like:
As for the RoR community not being familiar with Data Transfer objects - I agree, in that we rarely (if ever) use the term. As far as following the spirit of the principle though, any RoR developer worth his oats will already be doing so - keeping data manipulation and other convenience method declarations away from the view, stored in either helpers, libraries, or models. It's just that DTO's no longer need to be explicitly defined, as the structure of Rails is built around the MVC design principle so tightly that it forces them on you without even introducing what they are. March 9, 2010 EDIT: Most if not all of those concerns come from theoretical standpoints, and I believe you're not getting a good idea of what ActiveRecord really is. If you have something specific that you can't get ActiveRecord to do, post it here and I'll be glad to answer. :) Some specific comments:
In Java and .NET, a DTO is also an instance of a class. In most cases, it is a flattened version of your persistent/ActiveRecord model. It is generally not recommended to pass around an instance of your persistent models since it contains relationship information in the form of object associations. DTOs are used to fetch and project information that are only needed to save on round trips and excessive data fetching. My concern is if you always use and re-use persistent entities to project data to the screen, it will be full of invariants (just like the :invalid method you're proposing) I've actually adopted the concepts you're proposing in .NET using an ActiveRecord implementation. It is easy and straightforward like what it would have been in RoR but you'll probably have a lot of headaches when your views don't exactly map to your persistent/ActiveRecord models. This is where the ActiveRecord approach fails as a pattern as most object-oriented heads would say. It just doesn't scale if your problem domain is quite complex. I think we're veering off-topic, any specifics? Also please see my edit. Oh and this may not answer your questions directly, but it's an interesting read nonetheless: http://stackoverflow.com/questions/7864/why-all-the-active-record-hate Updated the question to show an example of Entity/AR to DTO mapping. I think I should treat my DTOs as ARs too and fetch data from denormalized database view-like objects. 1
I think you guys are debating on a "design" (aka more of an art) issue. You can always make something complicated or simpler regardless of what implementation framework you use. The question is not for your everyday CRUD needs, the setting is more on an enterprise level where models tend to be very complex as they are being used in different bounded contexts. First hand experience tells me the humble activerecord won't scale for very complex requirements, most especially if you have a lot of different screens where your AR will be used. 1
"Everything in RoR is a class, in essence, objects" This should be "everything in ruby is an object, including a class" :)
showing 5 of 8
show all
|
Looks like the RoR community does not have a concept of data transfer objects/view models. I asked the same question in stackoverflow with few takers ehehe
Most - or let's rephrase it this way - a lot of RoR heads are Web developers. And compared to the enterprise, the problem domain or scope that an average Rails developer will encounter is not on the scale as to warrant the use of DTO's. If you would also notice, the average RoR team is small - at around 3-4 devs working on a project - and in this setup, the benefits of having an extra layer of abstraction that a DTO provides may seem nil or non-existant.
I think you should ask first whether a DTO is needed or not. I have the impression that you believe the DTO is the right way to solve the problem you showed. For example, why is BusNO not part of your model? DriverName is part of your model though it is not a persistent information by itself but rather an aggregate of persistent attributes, or perhaps delegated to another model.
Perhaps you can give a better example because from what you've shown, I am not convinced that these aren't part of your model.