Implementing Domain Model and Data Mapper pattern with external data sources

Update:

Ok some update, finally we have decided on the framework (Yii) and have some initial structures. I will paste some of the relavent code to illustrate our current status: (only has read action here)

Controller

class SponsorController extends RestController
{
    public function actionRestView($id)
    {
        $sponsorMapper = new SponsorMapper(
            new Db1Adapter(), // Gateway to the external SOAP Webservice
            new Db2Adapter()  // Gateway to the external REST Webservice
        );

        $data = $sponsorMapper->read($id);

        $this->renderJson(
            array(
                'success' => true,
                'message' => 'Record Retrieved Successfully',
                'data' => $data
            )
        );
    }
}

Domain Model

class Sponsor extends CModel
{
    public $id;
    public $db1Result;
    public $db2Result;

    public function attributeNames()
    {
        return array(
            'id',
            'db1Result',
            'db2Result',
        );
    }
}

Data Mapper

class SponsorMapper extends Mapper
{
    public function __construct(SoapAdapter $db1Adapter,
                                RestAdapter $db2Adapter)
    {
        $this->adapters['soap'] = $db1Adapter;
        $this->adapters['rest'] = $db2Adapter;
    }

    public function read($id)
    {
        $db1Result = $this->adapters['soap']->demoRequest();
        $db2Result = $this->adapters['rest']->demoRequest();

        return $this->createEntity($db1Result, $db2Result);
    }

    protected function createEntity($db1Result, $db2Result)
    {
        $sponsor = new Sponsor();
        $sponsor->db1Result = $db1Result;
        $sponsor->db2Result = $db2Result;

        return $sponsor;
    }
}

Now I have 2 questions:

  • Right now the property of Sponsor object is just db1Result and db2Result and I will change it to the actual properties (eg. firstName, lastName, email) so the SponsorMapper::createEntity will be something like this:

    protected function createEntity($db1Result, $db2Result)
    {
        $sponsor = new Sponsor();
        $sponsor->firstName = $db1Result->result->first_name;
        $sponsor->lastName = $db1Result->result->last_name;
        $sponsor->email = $db2Result->QueryResult->ItemObject->email;
    
        return $sponsor;
    }
    
  • It seems to me that these kinds of stuff should happen inside the domain object instead mapper; I know I can treat db1Result and db2Result as domain object of their own and create their relations to Sponsor domain object, but I'm not sure if that's the right direction. Should I do it in the mapper?

  • I will need introduce a caching layer because to retrieve data from external databases is not fast. Where is the best place/practice to introduce caching layer? One way to do it is in the controller, so code caching layer as another mapper, and if this cache mapper doesn't return data, we can then go the long way to retrive stuff from external. But this solution means I will need to put all those logics all over the controller actions, maybe there is a better way to place caching layer?
  • Initial Question:

    We are in the process of designing/creating a RESTFul API project on top of PHP MVC framework. The project's goal is to serve as an adapter between two other "foreign" APIs (one SOAP, the other REST).

    I'm thinking the models in this project should roughly work like this:

  • Say we have a collection of user data on both "foreign" database behind their respective APIs, and locally we create a model "User"
  • Say we have a "GetById" method inside the "User" model, which needs to: grab the user data by id from both foreign APIs, combine and return the result.
  • I probably will use a builder pattern inside "User" model to instantiate 2 other models (for each foreign API), and ask those 2 models to fetch data. With this structure I can let the 2 additional models to inherit things need for them to communicate with their APIs.
  • So we have those models:

  • User
  • SOAPBuilder
  • RESTBuilder
  • UserSOAP
  • UserREST
  • Now what I don't like about this design is, the structure is a bit complex and I don't know a good way to place those model files into different places based on their "types" (the User model itself, the builder models, etc).

    Am I over-designing this? Is there better patterns I should consider?


    There are no "multiple models" in MVC. Model is one of two layers (along with presentation layer), which make up the MVC design pattern. What people usually call "models" are actually domain objects. You might find this relevant.

    That said, you are looking at this all the wrong way. What you currently have is variation of active record pattern. Only, instead of database, in this case storage medium is REST API and/or SOAP interface instead of classical SQL storage.

    The best option in this case would be to separate the domain object (for example: User ) from the different elements of storage related logic. I personally prefer to implement data mappers for this. Essentially, if you have a domain object, you pass it to the mapper's method, which then either retrieves information from said object and sends it to storage or retrieves the data from storage and applies it to that domain object.

    The User instance does not care whether it was save or not. It has no impact on domain logic.

    链接地址: http://www.djcxy.com/p/9616.html

    上一篇: 数据映射器仅适用于CRUD操作?

    下一篇: 使用外部数据源实现域模型和数据映射器模式