If I have two tables in a DB User and Userinfo (split for normalisation purposes) I generate the two models User, UserInfo and use them as normal via relationships.
Later on I have section of my application that reads and writes to both, however, there is a fair amount of business logic on creating entries for example looking up other tables for conditional rules, string building etc.
Would it make sense to make a third model (a non-database-backed model) to handle all this and to create/save via the other two models? or should I keep this in the controller?
Another example could be importing a CSV file where all the data is split between different tables, separate models as used by the rest of the application. Could I use a model defining each row that handles saving the imported data via the other models. Or again should this be in the controller?
I am interested in the best practices when developing large rails applications.
Sounds like you're normalizing (minimizing redundancy) rather than de-normalizing.
I don't know your application, so please take this as something to consider rather than a recommended best practice: what I typically like do in a situation like this is hide the Userinfo behind the User, so that the User is the only part of the application that even knows there is a Userinfo. This keeps things simple and consistent and DRY across the other clients of the code (controllers, other models, and you when you interact with it in the console).
Introducing a third model might serve the same purpose, but it's also adding conceptual weight to the application.
The answer depends on why you split the user data into two tables in the first place - what problem was it supposed to solve. Figure that out and try to apply the same logic to the models.
In any case, I agree that it makes sense to create a third model that encapsulates the complexity of working with the other two. This lets you present a simpler interface to other layers of the application (controllers, views). However, you'll have to watch carefully how far you're going with this. If you find yourself re-implementing most of ActiveRecord::Base by delegating calls to your encapsulated components, then it may be time to reconsider.
BTW, what you did isn't de-normalization. De-normalization in the context of a relational database means creating redundancy (check out the Wikipedia article on normalization, de-normalization is the opposite). This is usually done in order to improve performance by reducing the amount of joins required.
Related
I'm designing a small app as a way to learn Ruby on Rails which will (hopefully) display network traffic trends. Since the data I'm basing off is quite large (100s of millions of rows), we have three tables which sums up data by hour, by day and by month. I'm curious if the proper approach is to create three models, one for each type of data despite their similarities. It seems to be the way I'm leaning with the understanding I will eventually have to do some refactoring to help eliminate duplicate coding. Just curious if there were any other approaches that could be suggested?
You can consider using STI(single table inheritance). Read How (and When) to Use Single Table Inheritance in Rails for more details.
from the article:
STI should be considered when dealing with model classes that share much of the same functionality and data fields, but you as the developer may want more granular control over extending or adding to each class individually. Rather than duplicate the code over and over for multiple tables (and not being DRY) or forego the flexibility of adding idiosyncratic functionality or methods, STI permits you to use keep your data in a single table while writing specialized functionality.
I have an approach in designing MVC components which is to separate the Data Model (DB persistence) from the View Model of my component despite they represent theoretically the same element. I just map later the two models.
Do you think it's a good approach? Or I should try to make only one model?
When I run into issues like this, I try to create only the necessary classes that I need. This will help to keep the project smaller and to avoid confusion as to what class or object you are supposed to be using. I always try to picture the next guy coming and working on my code and what he would think of and where he would stumble in my logic. I would only use a ViewModel if you are creating objects from multiple Models you retrieved from the database.
Based on your statement it seems that you've created a duplicate data model which is mapped to a second model and would like to know if this is an optimal approach.
Recommendation
I don't think this is necessarily an optimal solution, but a lot depends on your use-case. What I typically do is create a data model that represents unique entities. Then I create a data management class that handles the interactions and use-cases of the data. The data manager would cover things like adding/removing custom objects from a collection. The approach I take is basically a lightweight approach to Apple's use of it's Core Data Framework (docs).
So for example one could use a dictionary, array, or set (or some combination of these) to manage the collection of custom objects together with a shared singleton object acting as a data manager and leveraging built-in archiving/unarchiving capabilities to handle a data graph requirements for an app. Actually,y the result is about the same as a simple use of Core Data, so I'd definitely recommend you get familiar with the standard approach used by Apple (it's embedded into every project template by default).
The good news is that once you choose an approach and develop it very carefully you could end up with a sharable resource that can be reused in many different projects. So for example, the data manager class might encapsulate the movement of data internally (files, local urls, etc) and externally (urls, soa, etc) and even deal with caching, serialization, etc.
I use only classes with get / set methods that operate a mapping of the DB and the VIEW. These are development policies. Using hybrid objects have a greater lightness of the project is in development at runtime. In some scenarios, there may be redundancy in the classes. It is important to aspire to the perfection of the code :-)
I'm working on a Ruby on Rails project. The project has a main database table, called 'Post'.
This table is used for storing questions, answers, comments and announcements as different post types. Currently we use only one model to access the data. But while working on the systems it feels cluttered. Because every post type has some differences.
What is a best practice:
Split the post resource into different resources, but using only the one Post table for data access?
Split the database table into comments, questions, answers and announcements tables; and use resources for each table (model+controller+views)?
Short answer: it depends.
What you're doing now is pretty close to Single Table Inheritance (STI), though it sounds like you're using a single Post class which contains all your different behaviors. STI is a valid approach to storing your model data, but like everything, it has advantages and disadvantages.
My recommendation would be to use separate classes to encapsulate the behavior of separate domain models. This is the fundamental paradigm in OOP, so I would say it's probably a best practice.
Assuming you split your behavior across classes, the question of whether you use a single table to store them or multiple tables really depends on the complexity of your queries and how many common fields there are across your models. If they share a lot of common fields, STI may work nicely. If not, you're probably not going to enjoy the overhead of all the extra reads/writes you'll pay. But all that is really secondary and can be figured out as your app grows and you learn more about the usage patterns.
Try it using STI and see how it goes. Splitting things up into multiple tables is easier than the opposite, so a migration path is not necessarily too hard.
I think the choise should be based on every certain situation. Just enumerate advantages and disadvantages of each option for you. Points to investigate and make decision could be like: number of differences between each type of entities, the way to access entities, frequency of usage of each type of entities, maintability of code, forecast of changes in the future, and many others.
Does anyone here use DTO's to transfer data from the controller to the view? If so, where would you recommend storing those files? /apps/dtos, and then let them mirror the views dir structure? Any recommendations on testing these animals with rspec?
Please don't listen to the other answers. They are terrible. Rails helpers are terrible. Using rails models everywhere is terrible. I am begging you, design your application properly and decide if you need DTOs or not. Decide if you actually want to have rails models to handle things other than communication with the database. Decide if you actually want to not have a layer between your app and rails and so on and so forth. Rails design is suitable for only small apps or apps that have to be developed super quickly. But if it's not something trivial and you expect to develop it for some time, please invest your time into proper design. Don't be afraid to break Rails conveniences. And may the force with you.
The Rails convention is not to use distributed tiers for controller and view layers. The separation is there, but it is logical and relatively thin/lightweight compared to the types of frameworks you see in Java land.
The basic architecture is that the controller sets instance variables that are available in the corresponding view. In the general case, the instance variables will be model instances or collections of model instances (coming from the database). Models should be the core of your business logic. Controllers coordinate flows of data. Views display it. Helpers are used to format display values in the view ... anything that takes a model value and does something just for display purposes (you may find that a helper method used repeatedly may actually be better off on the model itself).
However, if you find that a view needs knowledge of many different models, you might find it easier to wrap models into another object at a higher-level of abstraction. Nothing prevents you from creating non-active-record objects that collect and coordinate your actual AR models. You can then instantiate these objects in the controller, and have them available to the view. You generally have to be at a pretty dense level of complexity in the controller to need this type of thing.
I would tend to throw such objects into apps/models - Rails already loads everything in this directory, keeps things easy from a config/expectation point of view.
If you're coming from a .NET or J2EE background you may be thinking about patterns like DTO. You may or may not be surprised (and possibly happy) to learn that Rails doesn't do things that way by convention.
In particular there is no need at all to formally transfer (or store) serialized objects between the controllers and views. Instance variables (typically model attribute values) created in the controller are available within the view for free as provided by the framework without any additional programming effort needed.
What I've been told is that generally, this is work that is handled by 'helpers'. They basically help you format your model objects for view consumption from within the view. So it's definitely not a 1-1 mapping of concepts, but that's the thinking in the rails world
In the perfect application every business rule would exist only once.
I work for a shop that enforces business rules in as much as possible in the database. In many cases to achieve a better user experience we're performing identical validations on the client side. Not very DRY. Being a SPOT purist, I hate this.
On the other end of the spectrum, some shops create dumb databases (the Rails community leans in this direction) and relegate the business logic to a separate tier. But even with this tack, some validation logic ends up repeated client side.
To further complicate the matter, I understand why the database should be treated as a fortress and so I agree that validations be enforced/repeated at the database.
Trying to enforce validations in one spot isn't easy in light of conflicting concerns -- stay DRY, keep the database a fortress, and provide a good user experience. I have some idea for overcoming this issue, but I imagine there are better.
Can we balance these conflicting concerns in a DRY manner?
Anyone who doesn't enforce the required business rules in the database where they belong is going to have bad data, simple as that. Data integrity is the job of the database. Databases are affected by many more sources than the application and to put required rules in the application only is short-sighted. If you do this you will get bad data from imports, from other applications when they connect, from ad hoc queries to fix large amounts of data (think increasing all the prices by 10%), etc. It is foolish in the extreme to enforce rules only through an application. But then again, I'm the person who has to fix the bad data that gets into poorly designed databases where application developers think they should do stuff only in the application.
The data will live on long past the application in many cases. You lose the rules when this happens as well.
In way, a clean separation of concerns where all business logic exists in a single core location is a Utopian fantasy that’s hard to uphold
Can't see why.
handle all of the business logic in a separate tier (in Rails the models would house “most” of this)
Correct. Django does this also.
some business logic does end up spilling into other places (in Rails it might spill over into the controllers
Not really. The business logic can -- and should -- be in the model tier. Some of it will be encoded as methods of classes, libraries, and other bundles of logic that can be used elsewhere. Django does this with Form objects that validate input. These come from the model, but are used as part of the front-end HTML as well as any bulk loads.
There's no reason for business logic to be defined elsewhere. It can be used in other tiers, but it should be defined in the model.
Use an ORM layer to generate the SQL from the model. Everything in one place.
[database] built on constraints that cause it to reject bad data
I think that's a misreading the Database As A Fortress post. It says "A solid data model", "reject data that does not belong, and to prevent relationships that do not make sense". These are simple declarative referential integrity.
An ORM layer can generate this from the model.
The concerns of database-as-a-fortress and single-point-of-truth are one and the same thing. They are not mutually exclusive. And so there is no need what so ever to "balance them".
What you call "database-as-a-fortress" really is the only possible way to implement single-point-of-truth.
"database-as-a-fortress" is a good thing. It means that the database forces anyone to stay away who does not want to comply (to the truth that the database is supposed to adhere to). That so many people find that a problem (rather than the solution that it really is), says everything about how the majority of database users thinks about the importance of "truth" (as opposed to "I have this shit load of objects I need to persist, and I just want the database to do that despite any business rule what so ever.").
The Model-View-Controller framework is one way to solve this issue. The rails community uses a similar concept...
The basic idea is that all business logic is handled in the controller, and where rules need to be applied in the view or model they are passed into it by the controller.
Well, in general, business rules are much more than sets of constraints, so it's obvious not all business rules can be placed in the database. On the other hand, HLGEM pointed out that it's naive to expect the application to handle all data validation: I can confirm this from experience.
I don't think there's a nice way to put all of the business rules in one place and have them applied on the client, the server side and the database. I live with business rules at the entity level (as hibernate recreates them at the database level) and the remaining rules at the service level.
Database-as-a-fortress is nonsense for relational databases. You need an OODB for that.