1

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:

  1. 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?

  1. 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:

    1. User
    2. SOAPBuilder
    3. RESTBuilder
    4. UserSOAP
    5. 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?

Vincent
  • 25
  • 1
  • 6

1 Answers1

1

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.

Community
  • 1
  • 1
tereško
  • 56,151
  • 24
  • 92
  • 147
  • Thanks tereško for the answer. If I understand correctly, you are suggesting passing the domain objects (User) into mappers (UserSOAP, UserREST) and let the mappers manipulate/update domain object based on request. (mine was to encapsulate mappers into domain object) My question is what are the benefits? It seems to me this doesn't really change where most of the code goes to, and doesn't really change the complexity. – Vincent Aug 18 '12 at 06:21
  • 1
    @Vincent , the main benefit is that you gain the ability to test this all separately. Also, this way you gain a clear point for adding caching system. This way, if you have a mapper which can work save and retrieve data from SOAP and mapper which can save and retrieve data from REST, then you by design have system which can retrieve from REST and ave in SOAP. You would find an extremely simple API example [here](http://stackoverflow.com/a/11943107/727208). This whole thing is tied to [SRP](http://en.wikipedia.org/wiki/Single_responsibility_principle). – tereško Aug 18 '12 at 06:34
  • Showing some code might lead to better answers. Up till now i have been guessing what architecture you actually have hidden under the missused buzzword "model". – tereško Aug 18 '12 at 06:37
  • As mentioned earlier we are still at the planning stage, otherwise would love to show you some code. In fact we haven't picked the framework yet. In defence of the misused MVC (I probably will get crucified for this, but WTH), web MVC is very much different from its original structure, and depending on the framework, the interpretation & implementation are all different. Lots of them (if not most?) adopt the oversimplified "model = database table" concept. I think without considering the problem the developers are trying to solve but criticise the design isn't always constructive... – Vincent Aug 18 '12 at 06:50
  • @Vincent , you might find [this answer](http://stackoverflow.com/a/5864000/727208) to be an interesting read on subject of Web MVC. – tereško Aug 18 '12 at 06:55
  • Ya read that one and the User model in my answer is basically the domain object (edit: service is probably more accurate), UserSOAP and UserREST are the data mapper (whether I can or should build the data mappers of all SOAP logic into one data mapper, I'm not entirely sure yet). And it seems to me the concept of "Service" is so wide that it represents the notion of "model" in its loosest way. – Vincent Aug 18 '12 at 07:08
  • Say with your design, we probably will end up with UserService.php, SOAPMapper.php, RESTMapper.php, UserDomainObject.php. Should I chuck all of them into the /protected/models/ folder (assuming that's the folder names for whatever framework we end up with) ? – Vincent Aug 18 '12 at 07:12
  • Services are things like "authentication", "mail sender" or "document library" and they might manipulate same object. For example, authentication would deal with `User` objects to some extent, but so since each article in site could be made or commented by different user, the "library" service too would use the `User` object for some tasks. Services, as I understand them, are high level interfaces for domain logic and govern the interaction between domain logic and storage. – tereško Aug 18 '12 at 07:19
  • makes sense, domain object is more about an object; service are functions/features. – Vincent Aug 18 '12 at 07:25