11

I've been working with Sequelize.js recently and come across the term "DAO" pretty frequently. Coming from ActiveRecord (in Rails), the idea of an ORM seems pretty straight forward.

Could someone explain to me what a DAO is? How does it differ from an ORM? How does it result in more modular code/prevent abstraction leaking?

Edit: After reading things like: https://www.reddit.com/r/learnprogramming/comments/32a1fr/what_is_the_general_difference_between_dao_and_orm/

It feels/seems like a DAO could be thought of as a singular "model" - as in the context of ActiveRecord, my User instance would be considered a DAO in that it: "abstracts the implementation of a persistent data store away from the application and allows for simple interaction with it"?

The Dembinski
  • 1,469
  • 1
  • 15
  • 24
  • 1
    "Raw Queries" is a [specific example](http://docs.sequelizejs.com/manual/tutorial/models-usage.html#raw-queries) in which Sequelize.js mentions `DAO`: `// Are you expecting a massive dataset from the DB, // and don't want to spend the time building DAOs for each entry? // You can pass an extra query option to get the raw data instead: Project.findAll({ where: { ... }, raw: true })` – Nate Anderson Feb 18 '18 at 04:49

2 Answers2

28

Here's some thoughts which might help clarify it for you. I'm more familiar with ActiveRecord than Sequelize, so I'll run with that, but the concepts should be the same for both.

You have a database. You can, completely independent of Rails (eg. using a database admin tool), go and run a query on that database - something like "select * from users limit 1". But that just gives you a set of results in some admin window, which aren't much use to your Rails app. You want to be able to execute SQL from your Rails app, and get data back in a form that Ruby/Rails can work with. You need to Access your Data through some kind of ruby Object - you need a Data Access Object, or DAO.

In rails, you could run the query above with something like:

result = ActiveRecord::Base.connection.execute("select * from users limit 1")

The result variable won't know or care about your User model. All it will contain is essentially a list of plain ruby Hash instances, like:

{
  "id" => "1234",
  "email" => "fred@example.com",
  "first_name" => "Fred",
  "last_name" => "Flintstone",
}

If you wanted to update the first_name to Bob, you couldn't just edit that hash and call save on it - it's just a plain old hash, just the data and no extra smarts. So you'd have to write your own SQL again, and get Rails to execute it for you:

ActiveRecord::Base.connection.execute("update users set first_name = 'Bob' where id = 1234")

So what you're using in this context is basically just Rail's DAO, without using it's ORM.

The ORM is like a layer on top of the DAO. You can have a DAO without an ORM, but you can't have an ORM without a DAO. The ORM, or Object Relational Mapper will Map concepts / records in your Relational database with Objects in your programming language (ie, Ruby). So, if you wanted to do the stuff above, using Rail's ORM rather than using it's DAO, it might look like:

user = User.find(1234)
user.name = 'Bob'
user.save!

See how much nicer it is using an ORM? Now, the snippet above, using the ORM, will still essentially just execute the same SQL we detailed earlier. The ORM just abstracts away more of the details and provides smarter objects to save us a bunch of extra work.

Again, the concepts demonstrated are transferable to Sequelize / Javascript and other langs/frameworks.

So a DAO is just "an object that can execute SQL and return results in some basic data structure native to the programming language". An ORM will ultimately use a DAO to communicate with the database, but provides a whole lot more on top.

joshua.paling
  • 13,762
  • 4
  • 45
  • 60
  • 1
    Awesome response. Helped clarify a lot and confirmed some floating 'suspicions' in my mind. Thank you very much. – The Dembinski Jan 09 '17 at 17:26
  • Pretty decent answer with a little more perspective to add. Say technology changes and you decide to use a data store that wasn’t a RDBMS that ActiveRecord can be used with. Your entire codebase is brittle because of how tightly coupled to the specific backend it is. A DAO is basically a factory, a facade, an abstraction to allow your application to work in conceptual models while leaving the concerns of storing, retrieving, and munging the backend to the DAO. – andyfeller Oct 09 '21 at 02:09
2

I am not too familiar with ActiveRecord but yes, it sounds like your User instance is indeed a DAO.

The previous answer has some confusing aspects in it which I hope to clarify.

According to Oracle's description of the DAO pattern it:

  • separates a data resource's client interface from its data access mechanisms
  • adapts a specific data resource's access API to a generic client interface

The DAO pattern allows data access mechanisms to change independently of the code that uses the data.

That being said, manually executing a SQL statement using a language's SQL driver would almost never be referred to as a DAO. Firstly, it doesn't abstract the data access mechanism enough to be of any practical value. You couldn't, say, swap your DB for a NoSQL backend without having to reimplement ActiveRecord's execute method to map SQL queries into the new backend's API.

That being said, some practical examples:

// Cloudscape concrete DAO Factory implementation
import java.sql.*;

public class CloudscapeDAOFactory extends DAOFactory {
  public static final String DRIVER=
    "COM.cloudscape.core.RmiJdbcDriver";
  public static final String DBURL=
    "jdbc:cloudscape:rmi://localhost:1099/CoreJ2EEDB";

  // method to create Cloudscape connections
  public static Connection createConnection() {
    // Use DRIVER and DBURL to create a connection
    // Recommend connection pool implementation/usage
  }
  public CustomerDAO getCustomerDAO() {
    // CloudscapeCustomerDAO implements CustomerDAO
    return new CloudscapeCustomerDAO();
  }
  public AccountDAO getAccountDAO() {
    // CloudscapeAccountDAO implements AccountDAO
    return new CloudscapeAccountDAO();
  }
  public OrderDAO getOrderDAO() {
    // CloudscapeOrderDAO implements OrderDAO
    return new CloudscapeOrderDAO();
  }
  ...
}

As shown, the DAO factory sets up any connections with the underlying DB. This configuration is abstracted from the client.

// Interface that all CustomerDAOs must support
public interface CustomerDAO {
  public int insertCustomer(...);
  public boolean deleteCustomer(...);
  public Customer findCustomer(...);
  public boolean updateCustomer(...);
  public RowSet selectCustomersRS(...);
  public Collection selectCustomersTO(...);
  ...
}

As you can see, the DAO is meant to provide an object with a narrow interface to control and abstract access to the data layer. At any time, one could reimplement findCustomer to query a NoSQL DB and the calling code wouldn't need to change. Thus "The DAO pattern allows data access mechanisms to change independently of the code that uses the data."

The DAO illustrated above may use an ORM library in the implementation to map the DB objects into models of the calling code or the implementation may perform the mapping itself. In any case, if the interface were changed so that findCustomer returns a plain object and did no mapping, it would still be a DAO. A DAO does not need an ORM but often uses one.

An ORM can potentially perform all of its DB access using raw methods like the programming language's method for establishing TCP connections. So An ORM does not need a DAO but often uses one.

The fundamental feature of an ORM is that it maps DB objects into domain models. If an ORM library also handles communications with the DB, whether it uses a DAO or opens TCP connections to the DB, then it is itself also a DAO.

papiro
  • 2,158
  • 1
  • 20
  • 29