2

I recently started to study OOP and MVC but I have lot of problems already! :)

I would love a clarification about the list on this site http://c2.com/cgi/wiki?DomainObject

  1. recognize which [of their] references indicate aggregation and which ones indicate association
  2. copy themselves
  3. maintain business logic
  4. compare themselves to other domain objects of the same type
  5. facilitate other objects that choose to print or display them
  6. conduct tests on their domain information"
  7. identify themselves
  8. validate themselves

1: are they referring to a list of methods that return ID's of other domain objects?

7 & 8: what do they mean with "identify and validate themselves"? how should this work since identification and validation would probably require the access to the persistence layer?

I've read a lot in the past months but I can't actually say I have a solid idea of OOP/MVC :(

What is this validation? And why do I need it? And how is it supposed to be performed? If data comes from a database why would I need validation since persistent data is already valid?

With just my own insightfulness I made up this set of classes for each model entity I have:

// it holds Student information, it can also contain partial data
// (for updating just certain fields in the database)
interface Student
{
    getID($id)
    setID($id)
    getName()
    setName($name)
    getSurname()
    setSurname($surname)
}

interface StudentValidator
{
    validateInsert(Student $data)
    validateDelete(Student $data)
    validateUpdate(Student $data)
}

interface StudentMapper
{
    getByID($id)
    getAllByClassroomID($crid)
    updateByID(StudentValidator $v, int $id, Student $data)
    deleteByID(StudentValidator $v, int $id)
    deleteAllByClassroomID(StudentValidator $v, int $crid)
    insert(StudentValidator $v, Student $data)
}

how far is this from being decent code? :)

thanks in advance!!!

sveva
  • 107
  • 10

1 Answers1

4

Validation & stuff

The validation within a domain object would be restricted only to the business rules that it encompasses. For example, when calling $student->isValid(), the domain object would see, if the name and surname has been set and whether they do not contain number in strange places (like, "Frederick William III" is perfectly valid, while improbable, name).

That does not include data integrity checks (like checking whether student's email address has not already been registered, thus violating UNIQUE constraint). The data integrity is verified by mappers. If you are using PDO for persistence abstraction, then in case of integrity error it will throw an exception, which you can handle internally in the mapper and set an error state on the domain object which you were attempting to store.

Identification of self

The "identify themselves" is kinda murky description. I would assume that it would refer to such operation as comparing the objects (like: $studentA === $studentB to verify whether it's the same student).

The reference thing

recognize which [of their] references indicate aggregation and which ones indicate association

This bit talks about interaction between domain objects. Like calling $course->addStudent($student); which would define some sort of relationship between a Course and a Student instance. I am bit rust on the UML front, but I think this would indicate that:

  • course contains an aggregation of students
  • student is associated to a course

An simplified and kinda clumsy example, which illustrates all the issues that you were having would probably looks like this:

$student = new Student;
$studentMapper = new StudentMapper($pdo);

$student->setId(42);

if (!$studentMapper->fetch($student)) { 
   // error , missing student
   return;
}

// found the one with ID 42
$course = new Course;
$courseMapper = new CourseMapper($pdo);

$course->setId(31);

if (!courseMapper->fetch($course)) {
   // missing course
   return;
}

$course->addStudent($student);

if ($course->isValid() && $student->isValid()) {
   // check if there are free spots in course 
   // and student has prerequisites
   $courseMapper->store($course);
   $studentMapper->store($student);
}

Whether there is course-student double booking gets checked, when mappers try to store the data.

Note: you also might notice that this starts to smell like a case for transactions. That's one of the reasons why in large scale OOP codebases it is common to use Units of Work.

Note nr.2: keep in mind that "domain object" and "domain object collection" are two separate things. Same goes for persistence of these entities. Kinda like described in this old post.

Community
  • 1
  • 1
tereško
  • 56,151
  • 24
  • 92
  • 147
  • Thank you for your answer!! wouldn't be better to have validation in a different class? I'd be able to reuse it on different domain objects implementations. Unrelated, I liked $mapper->fetchInto($obj)... I was just wondering how to switch arbitrarily the class name while fetching instances from the mapper! I'm currently using $obj->fetch($className), but what you suggested is definitely better (if I understood you correctly ^_^). Regarding this, how to do it with collections? how about $studentMapper->fetchAll(new Student()) and use internally "clone"? – sveva May 12 '14 at 23:06
  • Nevermind, it would be fetchAll(new StudentCollection()) without the clone thing! correct? – sveva May 12 '14 at 23:15
  • Let's see if I understood you. In practice the validation rules duplicate rarely. And when they do, it's *mostly* thing you should be doing with [Filter API](http://uk3.php.net/manual/en/book.filter.php) anyway. Also, I have notice, that validators have a tendency for uncontrollable growth in practice. – tereško May 13 '14 at 05:42
  • The example was "simplified". In practice you wouldn't see `$student = new Student` anywhere at the service layer code. Instead you probably would be using factories. Which means that the same line would look like `$student = $this->domainFactory->create('Student');` which would also provide you with "pressure point", where you can decide whether it has to return an instance of `Student`, `Padawan` or `TestingBrick`. As long as they implements same interface, the mapper should also be able to populate and persist those instances. Same goes for creation of mappers. – tereško May 13 '14 at 05:50
  • Yes, I think you understood me correctly. The problem `$obj->fetch($className)` approach (which unfortunately is quite popular) is that you end up with mapper class *also having responsibilities of a factory*. It would clearly violate [SRP](http://en.wikipedia.org/wiki/Single_responsibility_principle) without gaining any pragmatic benefits. This also robs you of the ability to use several mappers in sequence on single object. For example `$user = new User; $session->fetch($user); $sql->fetch($user)` to populate the currently logged in user. – tereško May 13 '14 at 06:03
  • I was referring to moving the "class name string" outside the mapper itself `function fetchByID($id){ ... return $pdos->fetchAll(PDO::FETCH_OBJ, $this->className); }` having $this->className injected by the constructor. does it make sense? thank you!!!1 – sveva May 13 '14 at 06:17
  • As for collections, first of all, collection and simple-domain-object mappers should be separate. As for "how those domain objects get into collections", I have used two approaches: cloning instances internally and having a separate `buildItem()` method withing the collection. In last project I used the latter approach. I had an abstract `Collection` class which implemented `Iterator, \ArrayAccess` and some "managing the arrays" methods: http://laravel.io/bin/1bMwn ... (cont.) – tereško May 13 '14 at 06:18
  • (cont.) ... while the concrete classes implemented the `buildItem()` methods and various getters/setters for mapping conditions. Please keep in mind that it was from a rushed project (4000+ lines in about 2.5 weeks) – tereško May 13 '14 at 06:18
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/52566/discussion-between-sveva-and-teresko) – sveva May 13 '14 at 06:23