DAO, DTO, and BO confusion
Hello,
I have decided to use the DAO pattern for a project that I am working on. Unfortunately, the relationships between my BO's make the DAO's overlap quite a bit. This has caused a lot of issues, especially in the area of SPEED. I have read a few articles on DAO's that say DTO's shouldn't aggregate other DTO's. I understand the advantage of this, but am confused as to the best way to go about designing such a system.
My system has the following DTO's:
Script - Holds an SQL script
Database - Holds the connection information for a database
Report - Holds the formatting information for a report, as well as references to the Script and Database that are used to get the data for the report.
MenuItem - Represents a button on the menu; references a Report that it links to.
The problem here is that each MenuItem object aggregates a Report object, which aggregates a Database object and a Script object. This is all fine and dandy until the entire Menu is requested from the DAO. This effectively causes EVERYTHING in the database to be loaded into memory, since each MenuItem needs to load the Report it aggregates, which needs to load the Script and Database it aggregates.
The other problem this introduces is that my DAO's need instances of other DAO's in order to load their data. For example, the ReportDAO needs a ScriptDAO and a DatabaseDAO. The MenuDAO needs a ReportDAO.
I think the problem here is that my DTO's are also serving as my BO's. I am unsure of how to get around this, though. I don't understand what the relationship between BO's, DTO's, and DAO's is. I was under the impression that BO's compose DTO's and use DAO's to store/retrieve/delete data. If this is the case, then all BO's need instances of the DAO's. Is this correct? For example, if I redesigned my system to do this, it would look something like the following:
publicinterface ScriptDAO{
public ScriptDTO getScript(int id);
publicvoid saveScript(ScriptDTO dto);
publicvoid deleteScript(int id);
}
publicinterface DatabaseDAO{
public DatabaseDTO getDatabase(int id);
publicvoid saveDatabase(DatabaseDTO dto);
publicvoid deleteDatabase(int id);
}
publicinterface ReportDAO{
public ReportDTO getReport(int id);
publicvoid saveReport(ReportDTO dto);
publicvoid deleteReport(int id);
}
publicinterface MenuDAO{
public MenuItemDTO getMenuItem(int id);
publicvoid saveMenuItem(MenuItemDTO dto);
publicvoid deleteMenuItem(int id);
}
publicabstractclass DAOFactory{
publicstaticfinalint ORACLE = 0;
publicabstract ScriptDAO getScriptDAO();
publicabstract DatabaseDAO getDatabaseDAO();
publicabstract ReportDAO getReportDAO();
publicabstract MenuItemDAO getMenuItemDAO();
public DAOFactory getDAOFactory(int factory){
switch (factory){
case ORACLE:returnnew OracleDAOFactory();
default:thrownew RuntimeException("Unknown factory.");
}
}
}
publicclass ScriptDTO{
private Integer id =null;
private String sql =null;
// getter and setter methods
}
publicclass DatabaseDTO{
private Integer id =null;
private String hostname =null;
private Integer port =null;
private String instanceName =null;
private String username =null;
private String password =null;
// getter and setter methods
}
publicclass ReportDTO{
private Integer id =null;
private String name =null;
private String description =null;
private Integer scriptId =null;
private Integer databaseId =null;
// other variables
// getter and setter methods
}
publicclass MenuItemDTO{
private Integer id =null;
private String label =null;
private Integer reportId =null;
// getter and setter methods
}
publicclass ScriptBO{
private ScriptDTO dto;
public ScriptBO(ScriptDTO dto){
this.dto = dto;
}
// getter and setter methods that are the same as the DTO
}
publicclass DatabaseBO{
private DatabaseDTO dto;
public DatabaseBO(DatabaseDTO dto){
this.dto = dto;
}
// getter and setter methods that are the same as the DTO
}
publicclass ReportBO{
private ReportDTO reportDTO;
private ScriptDAO scriptDAO;
private DatabaseDAO databaseDAO;
public Report(ReportDTO reportDTO, ScriptDAO scriptDAO, DatabaseDAO databaseDAO){
this.reportDTO = reportDTO;
this.scriptDAO = scriptDAO;
this.databaseDAO = databaseDAO;
}
publicvoid setScript(ScriptBO script){
reportDTO.setScriptId(script.getId());
}
public ScriptBO getScript(){
return scriptDAO.getScript(reportDTO.getScriptId());
}
publicvoid setDatabase(DatabaseBO database){
reportDTO.setDatabaseId(database.getId());
}
public DatabaseBO getDatabase(){
return databaseDAO.getDatabase(reportDTO.getDatabaseId());
}
// other methods
// executes the sql on the database and applies formatting rules then returns the result
public ReportInstance getInstance(Map<String,String> parameters){
// connect to the database
// run the script
// format the results
return reportInstance;
}
}
publicclass MenuItemBO{
private MenuItemDTO menuItemDTO;
private ReportDAO reportDAO;
public MenuItemBO(MenuItemDTO menuItemDTO, ReportDAO reportDAO){
this.menuItemDTO = menuItemDTO;
this.reportDAO = reportDAO;
}
public ReportBO getReport(){
return reportDAO.getReport(menuItemDTO.getReportId());
}
publicvoid setReport(ReportBO report){
menuItemDTO.setReportId(report.getId());
}
public String getLink(){
// creates a URL that links to the report
}
}
(Note that I sacrificed efficiency and caching mechanisms in order to keep my code as brief as possible.)
This design just doesn't seem right to me because I have to pass the DAO's into the constructor. I thought one of the main goals of BO's was to totally abstract the datastore and to allow seemless interaction with the other BO's. As you can see in this example, the users of MenuItem don't know anything about a ReportDTO. They only see the ReportBO. This works out great except for the constructor...
Would someone mind banging me over the head and clearing up the haze?
P.S. Is it necessary to have a ScriptBO and DatabaseBO class even though they have exactly the same methods?
DAO versus BO, as I understand it, makes you decide between two alternatives:
(1) Put all persistence logic into a separate layer - use the DAO.
(2) Proper object-orientation means persistence objects know how to persist themselves - put persistence logic in BOs.
Some people say that (1) is a symptom of an "anemic domain model".
"DAO's that say DTO's shouldn't aggregate other DTO's" - I don't know where you read this. I suppose the definition of DTO is in question here. What's DTO for? To ferry data between layers. If the parent/child relationship is part of the data, and I say it is, then I'd disagree with that "rule".
I don't think I like your system design. Too coarse-grained. Makes me think that your UI layer knows about Script, which means it knows all about the persistence layer. Where's the layering and separation of concerns?
"my DAO's need instances of other DAO's in order to load their data" - I write DAOs for parent classes only. Child objects are loaded into their parents by the parent DAO. Problem solved. The alternative is a "chatty" model (e.g., one network roundtrip and another for children; or worse, one for each parent and n trips for n children.)
The more I look at your design, the less I like it. Your Database design sounds like you're going to have to do all the caching and pooling code yourself.
"This design just doesn't seem right to me because I have to pass the DAO's into the constructor. I thought one of the main goals of BO's was to totally abstract the datastore and to allow seemless interaction with the other BO's. As you can see in this example, the users of MenuItem don't know anything about a ReportDTO. They only see the ReportBO. This works out great except for the constructor..."
This design doesn't seem right to me, either. A BO should not have to know about the DAO. One thing that you're missing is a service layer. The View makes a request to the Controller, which invokes a Service to perform the desired action. The Service is the thing that knows about your domain model and the DAOs - neither the View nor the Controller ever touch DAOs. The Service worries about getting Connections to the database and making them available to the DAOs. It also handles all the transactional logic for commit/rollback.
Have a look at Spring:
http://www.springframework.org
Even if you don't like the framework, it makes some good recommendations for proper layered design.
Is your UI Swing or Web-based?
%
> "DAO's that say DTO's shouldn't aggregate other
> DTO's" - I don't know where you read this. I suppose
> the definition of DTO is in question here. What's
> DTO for? To ferry data between layers. If the
> parent/child relationship is part of the data, and I
> say it is, then I'd disagree with that "rule".
Thought about an obvious reason for this "rule": bringing in a deep object tree all at once will be a big cost in terms of bytes on the wire and memory.
The solution is something like "lazy loading": only bring over the keys and fetch the data when it's really needed.
Have a look at Hibernate:
hibernate.org
%
Thanks for your response duffymo.
The design that I put up is not the one that I am currently using. It is what I thought it was "supposed" to be like...which is why I am so confused because it seems like a stupid design...which you have confirmed. The main thing I am confused about is the relationship between BO's, services, and DTO's. In my current design, my DAO's are accessed by my services. The DAO's actually return the BO's themselves (so I guess my BO's are also serving as my DTO's). This all works smoothly because when I call Report.getScript(), I actually get a BO that I can use, not an id number or a DTO that doesn't have any functionality. The downside of this is that the MenuItem BO links to an instance of Report, which requires an instance of Script and an instance of Database. So when the whole menu is loaded, EVERYTHING is loaded.
I guess what I am looking for is an example that uses DAO's, DTO's, and BO's, and that has some overlap between the BO's (such that a BO requires a reference to one or more other BO's). Do you think you could give such an example or direct me to a website that has one? I have seen the examples given by IBM that are stored on sourceforge. They are very clear to me, but they don't demonstrate how to handle BO's that hold references to other BO's.
As I said previously, my current design works great except for the speed issue. This can be solved easily with lazy-loading, but I would still like to have a correct understanding of the DAO/DTO/BO relationship.
I found this code fragment on the spring framework website:
public class ExampleBusinessObject implements MyBusinessObject {
private ExampleDataAccessObject dao;
private int exampleParam;
public void setDataAccessObject(ExampleDataAccessObject dao) {
this.dao = dao;
}
public void setExampleParam(int exampleParam) {
this.exampleParam = exampleParam;
}
public void myBusinessMethod() {
// do stuff using dao
}
}
This business object requires a reference to a DAO. This is a strange concept to me. I have always programmed business objects to be independent of storage objects. I thought that was one of the main points of a DAO.
Ok. I have done some more research and I've drawn the following conclusions:
-DAO's create/use DTO's when accessing data stores
-Services use DAO's to get DTO's, then assemble BO's with them
-BO's make up the model
Therefore, in a service that uses the command pattern, I would design my system in the following way:
public class ScriptDTO {
private Integer id = null;
private String sql = null;
// getter & setter methods
}
public class DatabaseDTO {
private Integer id = null;
private String hostname = null;
private Integer port = null;
private String instanceName = null;
private String username = null;
private String password = null;
// getter & setter methods
}
public class ReportDTO {
private Integer id = null;
private String name = null;
private String description = null;
private Integer scriptId = null;
private Integer databaseId = null;
// getter & setter methods
}
public interface ScriptDAO {
public ScriptDTO getScript(int id);
public void saveScript(ScriptDTO dto);
public void deleteScript(int id);
}
public interface DatabaseDAO {
public DatabaseDTO getDatabase(int id);
public void saveDatabase(DatabaseDTO dto);
public void deleteDatabase(int id);
}
public interface ReportDAO {
public ReportDTO getReport(int id);
public void saveReport(ReportDTO dto);
public void deleteReport(int id);
}
public class ScriptBO {
private Integer id = null;
private String sql = null;
// getter & setter methods
// behavior methods
}
public class DatabaseBO {
private Integer id = null;
private String name = null;
private String description = null;
private Integer scriptId = null;
private Integer databaseId = null;
// getter & setter methods
// behavior methods
}
public class ReportBO {
private Integer id = null;
private String name = null;
private String description = null;
private ScriptBO script = null;
private DatabaseBO database = null;
// getter & setter methods
// behaviour methods
}
/* Loads a report and makes it accessible by the view */
public class GetReportAction extends MyAction {
public void doAction(HttpServletRequest request, HttpServletResponse response) {
DAOFactory factory = getDAOFactory();
ScriptDAO scriptDAO = factory.getScriptDAO();
DatabaseDAO databaseDAO = factory.getDatabaseDAO();
ReportDAO reportDAO = factory.getReportDAO();
ReportDTO reportDTO = reportDAO.getReport(request.getParameter("report_id"));
ScriptDTO scriptDTO = scriptDAO.getScript(reportDTO.getScriptId());
DatabaseDTO databaseDTO = databaseDAO.getDatabase(reportDTO.getDatabaseId());
ScriptBO script = new ScriptBO(scriptDTO);
DatabaseBO database = new DatabaseBO(databaseDTO);
ReportBO report = new ReportBO(reportDTO, script, database);
request.setAttribute("report", report);
}
}
The question here is...should the BO's take the DTO's in the constructor, or should the service construct the BO's from the DTO's (which seems like the better approach to me because the BO's won't be coupled to the DTO's).
Is this an accurate understanding?
> Thanks for your response duffymo.
>
> The design that I put up is not the one that I am
> currently using. It is what I thought it was
> "supposed" to be like...which is why I am so confused
> because it seems like a stupid design...which you
> have confirmed.
That's my opinion, anyway. I had no idea I was such an authority! 8)
> The main thing I am confused about is
> the relationship between BO's, services, and DTO's.
> In my current design, my DAO's are accessed by my
> services. The DAO's actually return the BO's
> themselves (so I guess my BO's are also serving as my
> DTO's). This all works smoothly because when I call
> Report.getScript(), I actually get a BO that I can
> use, not an id number or a DTO that doesn't have any
> functionality.
That's the way I do it, too:
Service interface > DAO interface, both operating on BOs.
> The downside of this is that the
> MenuItem BO links to an instance of Report, which
> requires an instance of Script and an instance of
> Database. So when the whole menu is loaded,
> EVERYTHING is loaded.
You sound like you need "lazy loading" and caching. This is what object/relational mapping tools like Hibernate can help with.
> I guess what I am looking for is an example that uses
> DAO's, DTO's, and BO's, and that has some overlap
> between the BO's (such that a BO requires a reference
> to one or more other BO's). Do you think you could
> give such an example or direct me to a website that
> has one? I have seen the examples given by IBM that
> are stored on sourceforge. They are very clear to me,
> but they don't demonstrate how to handle BO's that
> hold references to other BO's.
If you have a Parent that has a Collection of Child objects, the ParentDAO should get all its data and populate the Collection at the same time.
If you lazy load the Children, you only get primary keys and having some code that will access the data when it's needed. Not trivial to write.
> As I said previously, my current design works great
> except for the speed issue. This can be solved easily
> with lazy-loading, but I would still like to have a
> correct understanding of the DAO/DTO/BO relationship.
http://www.hibernate.org
%
> If you have a Parent that has a Collection of Child objects, the
> ParentDAO should get all its data and populate the Collection at the
> same time.
My problem here is that my parent objects do have child objects, but those child objects can be managed separately by an administrator, so they cannot simply be loaded by one DAO. I need to be able to load them in individually if I need to, which requires separate DAO's. This also means that my ParentDAO needs an instance of the ChildDAO in order to load the children in. This isn't that big of an issue. It came into play, though, when I was going to add a caching mechanism just by writing a wrapper on top of the other DAO's. I was just going to provide a "CachedDAOFactory" that takes an instance of some "ConcreteDAOFactory" and wraps it. This will not work properly because ConcreteDAOFactory's getParentDAO will return an object with references to a non-wrapped ChildDAO. This is easy to get around, but is nevertheless a problem.
Thank you for your responses. Having the DAO's load BO's directly seems to work nicely for my particular circumstance. I think I will stick with my current design for now.
