Разработка поддерживаемой системы RPC

Я работаю над веб-приложением, которое будет широко использовать методы AJAX для взаимодействия клиент/сервер... JSON-RPC. Zend Framework используется на стороне сервера, и он предлагает хороший JSON-RPC-сервер, который я бы хотел использовать.

Моя цель - архивировать поддерживаемую систему, которая предоставляет большой набор функциональных возможностей на стороне сервера для клиентской стороны (javascript) без ненужного дублирования кода. Я видел многочисленные сообщения в блогах и руководства по использованию сервера ZF JSON-RPC (здесь здесь и здесь), но все они были направлены на то, чтобы разоблачить небольшой, общедоступный API. Дублирование кода является обычным явлением, например, одно сообщение в блоге раскрывает следующий метод:

public static function setTitle($bookId, $title) {
    $book = new Nickel_Model_Book($bookId);
    $book->setTitle($title);
    $book->update();
    return true;
}

Мне не нравится тот факт, что есть два метода setTitle. Если сигнатура метода для одного изменяется, другая должна храниться в синхронизации... кажется кошмаром на ноу-хау, если ваш API обширен. Мне кажется, что должен быть один класс Book с одним методом setTitle.

Моя первоначальная мысль добавляет аннотацию docblock @export к методам/классам, которые я хочу опубликовать. Когда я решаю разоблачить метод setTitle, я просто добавляю аннотацию, а не новый метод.

Одна потенциальная проблема, которую я вижу, связана с сохранением объекта. На стороне сервера, для setTitle имеет смысл установить свойство title объекта... но не сохранять его в базе данных до тех пор, пока не будет вызван update(). Клиентская сторона, вызывающая setTitle, должна немедленно влиять на базу данных. Одним из возможных решений является изменение всех аксессуаров, так что они берут необязательный второй параметр, означающий модификацию, немедленно обновлять базу данных:

function setTitle($title, $persist = false) {
    $this->title = $title;

    if ($persist) $this->update();
}

Какой-то прокси-класс может гарантировать, что флаг $persist установлен для всех клиентских RPC-вызовов.

Другой проблемой является сериализация объектов PHP. На стороне сервера имеет смысл делать вызов $book->setTitle("foo") OO-стиля, но клиентская сторона book.setTitle(1234, "foo") имеет смысл (где 1234 - идентификатор книги) из-за отсутствия состояния. Моим решением для этого было бы, чтобы вышеупомянутый класс-прокси отвечал за то, что он превратил book.setTitle(1234, "foo") в:

$book = new Book();
$book->load(1234);
return $book->setTitle($title);

Я чувствую, что эта проблема, должно быть, обсуждалась или обсуждалась раньше... но я не нахожу много ресурсов в Интернете. Это похоже на разумное решение?

Ответы

Ответ 1

То, что вы ищете, называется Уровень сервиса.

Их объекты должны быть чисто контейнерами данных (если вы не используете Active Record), вы должны показывать только свой сервисный уровень, а это, в свою очередь, обращается к их объектам и их соответствующим методам.

"Your Book class is a Domain Model, you should now create your Service Layer"

Ваш класс обслуживания будет примерно таким:

class BookService extends Service {

    //...

    public function changeBookTitle( $title, Book $book )
    {
        //verify if the $title is correct and valid
        $book->setTitle( $title );
        $this->methodToGetThePersistenceManager()->save( $book );

        //fire events, create a result object, etc...
    }
}

Ответ 2

Я обдумывал ваш вопрос в течение нескольких минут. Если вы хотите попробовать, и это в PHP, вы можете сделать некоторые магические методы формы

множество {Свойство} из {Объект}.

Вы можете сделать это с помощью магического метода __call.

Таким образом, вам не нужно явно определять методы, но свойства все равно будут установлены.

Магический метод определит, какой тип объекта он должен создать, а какое свойство установить на нем, а затем сохранить.

Вам нужно будет найти способ дифференцирования между удаленными вызовами и локальными вызовами, хотя вы не будете упорствовать по ошибке, если выполняете вызовы локальной библиотеки и что вы сохраняете удаленные вызовы.

Я чувствую, что каким-то образом я неправильно понял ваш вопрос, но я попробовал.

Ответ 3

Ну,

То, что вы хотите делать на самом деле хорошо, на бумаге, и вы хотите, чтобы код поддерживался, и, в зависимости от вашей реальной системы, то, что вы просите, может даже быть несколько безопасным, однако оно приближается к тому, чтобы быть опасным и подверженным ошибкам.

Во всяком случае, вам, вероятно, придется самому сделать большую часть этого, или просто вставьте какой-то перехватчик во все вызовы методов, вот быстрый пример:

class Book {
  public $title = '';
  public $id = 0;
  public function setTitle($string) {
    $this->title = $string;
    echo "title called with $string\n";
  }
  public function twoArgs($arg1,$arg2) {
    echo "Multi-call: $arg1,$arg2\n";
  }
}
class Api {
  public function parse($json) {
    $jsonobj = json_decode($json,true);
    foreach ($jsonobj as $key=>$value) {
      $class_name = $key;
      $obj = new $class_name();
      foreach ($value as $vkey=>$vvalue) {
        if (method_exists($obj,$vkey)) {
          call_user_func_array(array($obj,$vkey),$vvalue);
        } else if (isset($obj->$vkey)) {
          echo "Setting $vkey\n";
          $obj->$vkey = $vvalue;
        }
      }
    }
  }
}

$json = json_encode(array('Book' => array('id' => 1234, 'setTitle' => array('The new title'))));
$api = new Api();
$api->parse($json);
$json = json_encode(array('Book' => array('id' => 1234, 'twoArgs' => array('arg1 :) ', 'arg2 :]'))));
$api->parse($json);

Очевидно, что вы хотите добавить логику, чтобы обрабатывать флаг persist, а также загружать или разрешать им передавать в конструкторе: [args] и обрабатывать это. и т.д. Когда речь заходит об экспонировании функций, вы раскрываете их в документации, они все доступны до тех пор, пока они являются общедоступными функциями с этой точки и т.д.