Reputation: 4368
Is it better to delegate some parts of a DAO to another DAO so that it will be more readable and compact? As well as less duplicate code?
Example
class StudentDAOImpl implements StudentDAO {
public Student findById(int studentId) {
// some code here to get student
}
}
class SemesterEnrollmentImpl implements SemesterEnrollmentDAO {
public SemesterEnrollment findSemesterEnrollmentByStudent(int studentId) {
// delegating finding of student to StudentDAO
StudentDAO studentDao = new StudentDaoImpl();
Student student = studentDAO.findById(studentId);
// code to get SemesterEnrollment using student instance
}
}
Because I read that DAOs should:
Does this mean I have to repeat the same process in every DAO instead of delegating just so it would be independent of other DAOs?
Upvotes: 2
Views: 2495
Reputation: 191
You know the rules of thumb, but no one is addressing the reasons why.
The role of a DAO is to contain a set of CRUD functions. Each method is some operation against a transactional data store (usually a database). If you chain DAO calls together, you end up chaining transactions together. This is not a big deal for READS; but when you get into WRITES, it gets messy.
For example, you have a composite EnrollmentRecord object. It contains a StudentRecord, SectionRecords for each class the student is enrolled in, and each SectionRecord links to a ClassRecord. If you call createEnrollement(EnrollmentRecord), it could also chain out to include the other objects in the EnrollmentRecord.
What happens if you can create the StudentRecord, but some of the SectionRecords or ClassRecords fail? Do roll back? Do you leave the student, but remove any enrolled classes? If one enrolled class fails, do you roll them all back?
Once you start coordinating transactions, you are now mixing in business logic. The business rules will change over time and situations; but the basic CRUD for Enrollment, Student, Section, and Class will not.
Use a Service object to aggregates DAOs. Composite data objects should be managed by a Service that coordinates all the calls to DAOs (event multiple calls to a single DAO!) This allows you to separate the business/transaction logic from the CRUD logic and keep each layer modular and reusable.
Another big reason to use a service layer is that entity models are usually bi-directional. You could load a student and then fetch all their classes. You could fetch a class and load all of its students. As your object model grows, you will easily get circular relationships.
For example you have objectA which contains a collection of objectBs. Your loadA method calls out to loadB.
In the future someone adds expands loadB to delegate to LoadC. Later, someone says it would be great if I could load a 'C' and know which 'A's it links to. So they expand loadC to delegate to that loadA which returns an 'A' object. Now the whole thing explodes and it isn't obvious without tracing the whole circular path.
Use a Service layer to coordinate composite objects. Separating composition logic from CRUD logic makes it easier to avoid circular references and manage complex object graphs.
So DAOs calling DAOs itself isn't evil; but, it is the kind of short cut that easily leads to giant DAO methods that handle CRUD, business/transaction logic, and complex object graph rules. Clean separation of DAOs from Services helps prevent this.
Upvotes: 2
Reputation: 41310
Short answer: No
DAOs which can be called Table Module are limited to one table per class according to the pattern
A Table Module organizes domain logic with one class per table in the data-base, and a single instance of a class contains the various procedures that will act on the data
However, these can be wrapped by a facade or Transaction Script to manage the interaction between multiple Table Modules.
A Transaction Script organizes all this logic primarily as a single procedure, making calls directly to the database or through a thin database wrapper. Each transaction will have its own Transaction Script, although common subtasks can be broken into subprocedures.
Upvotes: 2
Reputation: 386
The main issue here, in your example, is that you are using the SemesterEnrollmentDAO to search for another entity.
Do you absolutely need to retrieve the Student
entitiy to retrieve the SemesterEnrollment
later on?
If your entities have a nicely designed relationship between them, you could make a query for the SemesterEnrollment
, using the studentId
wihout having to retrieve the Student
entity first.
Example
Query query = em.createQuery("SELECT e FROM SemesterEnrollment e JOIN e.student s "
+ "WHERE s.studentId = :studentId");
This way you avoid both, delegating and the 1+N issue, if what you really needed to retrieve was the SemesterEnrollment
entity, as should the SemesterEnrollmentDAO
.
Upvotes: 1
Reputation: 1769
As a rule of thumb DAO's should be concerned with retrieving/persisting data so any business logic or data transformation tipically resides in a separate business layer. In the example you provide one could ask what happens if the call to StudentDAO fails because there is no data at all. That is the kind of problem one solves in the service layer. In your example the EnrollmentDAO could be turned into an EnrollmentService.
Upvotes: 0
Reputation: 401
A common thing is to use services to call the different daos' need to inject your data. This seems more logical in this case also because it is in fact business logic and imo it is better to have as little as possible of this in your database layer
Upvotes: 5