开发可维护的RPC系统

发布于 2024-10-03 19:16:16 字数 1707 浏览 3 评论 0 原文

我正在开发一个 Web 应用程序,该应用程序将广泛使用 AJAX 技术进行客户端/服务器通信……特别是 JSON-RPC。 Zend Framework 正在服务器端使用,它提供了一个我想使用的很好的 JSON-RPC 服务器。

我的目标是构建一个可维护的系统,向客户端 (javascript) 公开服务器端功能的大量子集,而无需重复代码。我看过许多关于如何使用 ZF JSON-RPC 服务器的博客文章和教程(请参阅此处和< a href="http://sourcecodebean.com/archives/creating-a-json-rpc-service-using-zend-json-server/422" rel="nofollow">此处),但它们都似乎旨在公开一个小型的、可公开使用的 API。代码重复很常见,例如一篇博客文章公开了以下方法:

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

我不喜欢有两个 setTitle 方法。如果一个方法签名发生变化,另一个就必须保持同步……如果您的 API 很广泛,这似乎是可维护性的噩梦。在我看来,应该有一个 Book 类,带有一个 setTitle 方法。

我最初的想法是向我想要公开的方法/类添加一个文档块注释 @export 。当我决定公开 setTitle 方法时,我只是添加注释而不是新方法。

我发现的一个潜在问题涉及对象持久性。在服务器端,setTitle 设置对象的标题属性是有意义的……但在调用 update() 之前不会将其保留在数据库中。在客户端,调用 setTitle 应立即影响数据库。一种潜在的解决方案是修改所有访问器,以便它们采用可选的第二个参数,表示修改应立即更新数据库:

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

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

某种代理类可以确保为所有客户端设置 $persist 标志 -端 RPC 调用。

另一个问题是 PHP 对象的序列化。在服务器端,进行 OO 风格的 $book->setTitle("foo") 调用是有意义的,但在客户端 book.setTitle(1234, "foo") 调用是有意义的由于缺乏状态, 是有意义的(其中 1234 是书的 ID)。我的解决方案是让上述代理类负责以某种方式将 book.setTitle(1234, "foo") 转变为:

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

我觉得这个问题之前必须已经解决或讨论过。 .但我在网上找不到很多资源。这看起来是一个明智的解决方案吗?

I am working on a web application that will make extensive use of AJAX techniques for client/server communication...JSON-RPC specifically. Zend Framework is being used server-side, and it offers a nice JSON-RPC server that I would like to use.

My goal is to architect a maintainable system that exposes a large subset of server-side functionality to the client-side (javascript), without unnecessary code duplication. I've seen numerous blog posts and tutorials on how to use ZF's JSON-RPC server (see here and here), but they all seemed geared towards exposing a small, publicly consumable API. Code duplication is common, for example one blog post has the following method exposed:

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

I don't like the fact that there are two setTitle methods. If the method signature for one changes, the other has to be kept in sync...seems like a maintainability nightmare if your API is extensive. It seems to me that there should be one Book class, with one setTitle method.

My initial thought is add a docblock annotation @export to methods/classes that I want exposed. When I decide to expose the setTitle method, I just add the annotation rather than a new method.

One potential problem I see involves object persistence. Server-side, it makes sense for setTitle to set the object's title property...but not persist it in the database until update() is called. Client-side, calling setTitle should immediately affect the database. One potential solution is to modify all accessors such that they take a optional second parameter signifying the modification should update the database immediately:

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

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

Some sort of proxy class could ensure that the $persist flag is set for all client-side RPC invocations.

Another problem is the serialization of PHP objects. Server-side, it makes sense to do a OO-style $book->setTitle("foo") call, but client-side book.setTitle(1234, "foo") makes sense (where 1234 is the book's ID) due to the lack of state. My solution for this would be to have the aforementioned proxy class be responsible for somehow turning book.setTitle(1234, "foo") into:

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

I feel like this problem must have been tackled or discussed before...but I'm not finding many resources online. Does this seem like a sane solution?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

又怨 2024-10-10 19:16:16

您要寻找的内容称为服务层

它们的实体应该是纯粹的数据容器(除非您使用 Active Record),您应该只公开您的服务层,而这又会访问它们的实体及其各自的方法。

您的服务类别类似于这:

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...
    }
}

What you're looking for is called Service Layer.

Their entities should be purely data containers (unless you're using Active Record), you should only expose your service layer, and this in turn accesses their entities and their respective methods.

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

Your class of service would be something like this:

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...
    }
}
紙鸢 2024-10-10 19:16:16

我思考了你的问题几分钟。如果你想尝试一下,而且是用 PHP 编写的,你可以创建一些

set{Property}of{Object} 形式的神奇方法。

您可以通过神奇的 __call 方法来做到这一点。

这样您就不必显式定义方法,但属性仍然会被设置。

这个神奇的方法将识别它必须实例化哪种类型的对象以及要在其上设置哪个属性然后持久化。

不过,您必须找到某种方法来区分远程调用和本地调用,以便在进行本地库调用并且确实坚持远程调用时不会错误地坚持下去。

我觉得我以某种方式误解了你的问题,但我尝试过。

I was mulling over your question for a few minutes. If you want to try, and it's in PHP, you could make some magic methods of the form

set{Property}of{Object}.

You can do this through the magic __call method.

That way you wouldn't have to explicitly define the methods but the properties would still get set.

The magic method would identify which type of object it has to instantiate and which property to set on it and then persist.

You'd have to find some way of differentiating between remote calls and local calls though so you don't persist by mistake if you're making local library calls and that you do persist on remote calls.

I feel like I misunderstood your question somehow but I tried.

淡淡绿茶香 2024-10-10 19:16:16

好吧,

您想要做的事情在纸面上听起来非常好,并且您希望代码是可维护的,并且根据您的实际系统,您所要求的甚至可能有些安全,但是它接近于危险且容易出错。

无论如何,您可能必须自己完成大部分工作,或者只是在所有方法调用中插入某种拦截器,这是一个简单的示例:

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);

显然您需要添加逻辑来处理持久标记,并加载或允许它们传入一个构造函数:[args] 并处理它。当谈到公开函数时,您可以在文档中公开它们,只要从那时起它们是公共函数,它们都可以访问,等等。

Well,

What you are wanting to do sounds really good on paper, and you want code to be maintainable, and depending on your actual system, what you ask may even be somewhat safe, however it's getting close to being dangerous and error prone.

Anyway, you would probably have to do most of this yourself, or just insert some sort of interceptor to all method calls, here's a quick example:

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);

Obviously you'd want to add in logic to handle persist flagging, and loading or allow them to pass in a constructor: [args] and handle that. etc. When it comes to exposing functions, you expose them in documentation, they are all accessible as long as they are public functions from that point on etc.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文