Dashboard > ringside > Home > Layering and Caching Design
Layering and Caching Design Log In | Sign Up   View a printable version of the current page.

Layering Strategy

The goal of our layering strategy is to

1. Seperate Concerns - like any good design, delegate authority to the correct classes

2. Seperate Implementations - Users should be able to easily replace or extend core ringside functionality, specifcally in the API module. 

3. Out of box implementation will use Doctrine at the Data Layer in order to 1. allow us to use Objects in code and 2. be more database agnostic.

The design we will use is the following:

Three layers

1. API

2. BO

3. DAO

API

api->bo->dao->DB/Service/Directory

The api layer is responsible for validating incoming params, returning protocol specific error codes, validating outgoing data and serialization/unserialization of data across the wire.

BO  

The BO is the public interface to a Module.  The BO can access n number of DAOs that make up this Module.  If the BO needs to access data outside of itself (the module) then it needs to call a public function on another BO. 

DAO

The DAO layer is responsible for CRUD operations on Doctrine Records.  This can be against individual Records or against an Object Graph. 

Layering Rules Ringside API Follows

This section lists the rules and reasoning for implementing Layering.

  1. DAOs should have no state.  BOs should not have state, should work like Stateless EJBs.
  2. All DAO functions should be static
    1. This enforces the No State Rule. 
  3. CRUD naming scheme
     action<object>
     Examples:
      deletePayment
      createPayment
    1. Makes reading code easier
    2. allows a DAO to have multiple create functions depending on it's implementation and the Object Graph it's working with.
  4. Select * is bad
    1. It's hard to tell in code what you are retrieving
    2. You really don't know what you are getting, if someone adds large fields itcould adversly affect performance.  Don't depend on Doctrine to do the right thing here, be explicit.
  5. All records will use timestampable - created and last_modified
    1. Makes the setting and usage of Created and Last_Modified consistent across all Records
  6. All records will use deleteable - will have a status field
    1. Will make Record state more Ovious
    2. We really don't want to delete data real time.  Instead an ETL tool should be used to determine if data needs to be moved or permanently deleted based on Business Intelligence rules.
  7. BOs are allowed to work Doctrine Objects
  8.  APIs are not allowed to doctrine objects, instead should work with Array Data Structures.  use ->toArray() on Doctrine Records or Collections.
  9. Any record in a module can be joined with any other record in a module.  If a BO needs to access data outside of itself (module)
     then it needs to access it via a BO interface and not a DAO directly.  This ties one module to another via it's public interface and not
     it's specific data implementation.
  10. generated directory is for generated classes.  Records directory is for the Doctrine Classes we have and is where we should customize them.
  11. Every table needs to have an autoincrement id field - it's possible to use compound keys with Doctrine, but seems to fail on inserts.  However
     using an ID is good practice and you can set up unique index constraints using Doctrine, see #19 Below.
  12. If a field has a default value in the DB make sure to explicitly set the field to NULL on the Doctrine Object. 
    1. Doctrine doesn't seem to do the right thing here on Timestamp fields when mysql is in strict mode.
  13. Vendor specific error messages FB_* for example should be used at the API layer only, BO and DAO layers should use RS_* specific error codes.
    1. This allows us to return families of error codes, Constrain Violations, Parameter Missing, etc.. and let's the specific API implementation decide how to deliver that response to the calling application.
  14. BOs are Modular objects that connect to n number of DAOs
  15. DAOs are CRUD against one or more Tables in the DB.  Typically this happens when using an object graph.
    1. User Profile is a good example of this, the actual profile is made up of many tables.
  16. BOs have a published public interface and are accessed via Jason's Service Implementation
    1. INSERT LINK
  17. All DAOs and insert scripts (tests, default data) need to work when mysql is in strict mode
    1. http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html
  18. Example Doctrine Check - do this in the Record class
    1. $this->check('price > discounted_price');
  19. Example unique constraint
class MultipleIndexTest extends Doctrine_Record
{
    public function setTableDefinition()
    {
        $this->hasColumn('name', 'string');
        $this->hasColumn('code', 'string');
        $this->hasColumn('age', 'integer');
        $this->index('myindex', array(
                      'fields' => array(
                                  'name' =>
                                  array('sorting' => 'ASC',
                                        'length'  => 10),
                                  'code'),
                      'type' => 'unique',
                      ));
    }
}
&nbsp;&nbsp;

Caching With Doctrine 

Doctrine works with PHP's in memory cache called APC.  It is a PECL extension that we may or may not want to ship with the product. I suggest we support all of the caches that Doctrine supports:
 
1. Memcache
2. APC
3. DB - Specifically they state sqllite, but sounds like they could/do support others.  Need to look at code for that.
 
Here is how you setup caching in Doctrine, it's very easy and flexible.
 
There are two types of cache:
 
1. Query Cache
2. Result Cache
 
Each is configured differently at different times.
 
There are different levels of caching:
 
1. Manager Cache - This is a top level global cache setup
2. Query Cache - This allows you to override the global cache options for a specific query.  User info for instance really only needs to happen once for the lifetime of a users session.
 
There are 4 or 5 different cache schemes that doctrine works with out of the box.  sqllite, memcache, apc, and some others.  Again, off the top of my head, it's literally something like:
 
$cacheDriver = new Doctrine_Cache_Apc();
$manager = Doctrine_Manager::getInstance();
$manager->setAttribute(Doctrine::ATTR_QUERY_CACHE, $cacheDriver);

Each cache driver is setup differently, for instance memcache is a poolable resource so you give Doctrine an array of memcache servers to use.  There are also a lot of options for cache timeouts and whatnot.
 
Long story short this means we can put all of this configuration into LocalSettings if we wanted to, it's very flexible and easy to turn off or on at runtime.
 
As an aside, here is the section on improving performance.  They have some tips that are general to PHP.
 http://www.phpdoctrine.org/documentation/manual/0_10?one-page#improving-performance

Resources

pear install pear.phpdoctrine.org/Doctrine-0.10.4

If that doesn't work try the following:

pear channel-discover pear.phpdoctrine.org
pear install http://pear.phpdoctrine.org/get/Doctrine-0.10.4.tgz

Constraints Allowed in Doctrine
http://www.phpdoctrine.org/documentation/manual/0_10?one-page#basic-schema-mapping:constraints-and-validators

Added by Mark Lugert , last edited by Mark Lugert on Jun 02, 2008  (view change)
Labels: 
(None)