Upgrade to Pro — share decks privately, control downloads, hide ads and more …

設計の考え方 - インターフェースと腐敗防止層編 #phpconfuk / Interface...

設計の考え方 - インターフェースと腐敗防止層編 #phpconfuk / Interface and Anti Corruption Layer

2024/06/22 開催の「PHP カンファレンス福岡 2024」(https://phpcon.fukuoka.jp/2024/ )のトーク資料です。

詳細:https://fortee.jp/phpcon-fukuoka-2024/proposal/25f1e7b5-937f-4de9-a6de-b78fc9efd71c

Shohei Okada

June 22, 2024
Tweet

More Decks by Shohei Okada

Other Decks in Programming

Transcript

  1. ① そのまま php-ulid を使った例 <?php use Ulid\Ulid; readonly class User

    { private function __construct( public string $id, public string $name, ) { } public static function create(string $name): static { $id = (string)Ulid::generate(); return new User($id, $name); } }
  2. <?php use Ulid\Ulid; readonly class User { private function __construct(

    public string $id, public string $name, ) { } public static function create(string $name): static { $id = (string)Ulid::generate(); return new User($id, $name); } } ① そのまま php-ulid を使った例
  3. ② 1 枚薄い wrapper を噛ませた例 <?php namespace MyUlid; use Ulid\Ulid;

    function ulid(): string { return (string)Ulid::generate(); }
  4. ② 1 枚薄い wrapper を噛ませた例 <?php namespace MyUlid; use Ulid\Ulid;

    function ulid(): string { return (string)Ulid::generate(); }
  5. ② 1 枚薄い wrapper を噛ませた例 <?php namespace MyUlid; use Ulid\Ulid;

    function ulid(): string { return (string)Ulid::generate(); } <?php use function MyUlid\ulid; readonly class User { private function __construct( public string $id, public string $name, ) { } public static function create(string $name): static { $id = ulid(); return new User($id, $name); } }
  6. ② 1 枚薄い wrapper を噛ませた例 <?php namespace MyUlid; use Ulid\Ulid;

    function ulid(): string { return (string)Ulid::generate(); } <?php use function MyUlid\ulid; readonly class User { private function __construct( public string $id, public string $name, ) { } public static function create(string $name): static { $id = ulid(); return new User($id, $name); } }
  7. 影響は wrapper までに留められる(※) MyUlid User uses php-ulid uses Post Comment

    …… uses uses uses ※ただし wrapper のインターフェース  が変わらない前提
  8. 影響は wrapper までに留められる(※) MyUlid User uses php-ulid uses Post Comment

    …… uses uses uses ※ただし wrapper のインターフェース  が変わらない前提
  9. 影響は wrapper までに留められる(※) MyUlid User uses php-ulid uses Post Comment

    …… uses uses uses ※ただし wrapper のインターフェース  が変わらない前提
  10. プログラミング言語の機能として提供される抽象型 狭義のインターフェース <?php namespace Psr\Http\Client; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; interface

    ClientInterface { /** * Sends a PSR-7 request and returns a PSR-7 response. * //(略) */ public function sendRequest(RequestInterface $request): ResponseInterface; }
  11. 所定の形式で HTTP リクエストを行う処理がある • 実際の HTTP リクエスト部分には Guzzle を用いる •

    wrapper を噛ませて利用者は Guzzle を意識しないよ うにしたい https://github.com/guzzle/guzzle 課題設定 2
  12. GuzzleHttp\ClientTrait /** * Create and send an HTTP GET request.

    * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. * * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. * * @throws GuzzleException */ public function get($uri, array $options = []): ResponseInterface { return $this->request('GET', $uri, $options); }
  13. GuzzleHttp\ClientTrait /** * Create and send an HTTP GET request.

    * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. * * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. * * @throws GuzzleException */ public function get($uri, array $options = []): ResponseInterface { return $this->request('GET', $uri, $options); } URI の文字列か、PSR に則っているので ここはそのまま使うという選択肢は充分ありえる
  14. GuzzleHttp\ClientTrait /** * Create and send an HTTP GET request.

    * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. * * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. * * @throws GuzzleException */ public function get($uri, array $options = []): ResponseInterface { return $this->request('GET', $uri, $options); }
  15. このような wrapper を噛ませても <?php use GuzzleHttp\Client; class MyHttpClient { public

    function get(string $url): string { $response = (new Client())->get($url); return $response->getBody()->getContents(); } }
  16. 実際に使おうとすると <?php use GuzzleHttp\Exception\GuzzleException; readonly class SomeService { public function

    __construct(private MyHttpClient $client) { } public function getSomeDataFromWeb(string $url): string { try { $responseBody = $this->client->get($url); } catch (GuzzleException) { // なんらかのエラー処理 } // 後続処理 } }
  17. まだ GuzzleHttp へ直接依存してしまっている <?php use GuzzleHttp\Exception\GuzzleException; readonly class SomeService {

    public function __construct(private MyHttpClient $client) { } public function getSomeDataFromWeb(string $url): string { try { $responseBody = $this->client->get($url); } catch (GuzzleException) { // なんらかのエラー処理 } // 後続処理 } }
  18. GuzzleHttp\ClientTrait /** * Create and send an HTTP GET request.

    * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. * * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. * * @throws GuzzleException */ public function get($uri, array $options = []): ResponseInterface { return $this->request('GET', $uri, $options); } GuzzleHttp 固有の例外なので 腐敗防止層では適切にハンドリングして • PHP 組み込みの例外に変換 • 独自定義の例外に変換 • 正常系として適切に処理 等の対応をしておきたい
  19. GuzzleHttp\ClientTrait /** * Create and send an HTTP GET request.

    * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. * * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. * * @throws GuzzleException */ public function get($uri, array $options = []): ResponseInterface { return $this->request('GET', $uri, $options); }
  20. GuzzleHttp\ClientTrait /** * Create and send an HTTP GET request.

    * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. * * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. * * @throws GuzzleException */ public function get($uri, array $options = []): ResponseInterface { return $this->request('GET', $uri, $options); } 確かに型情報としてはGuzzleHttpに依存していないが......
  21. 裏にある詳細を隠蔽する仕組み • HTTP • TCP/IP • VM • コンパイラ •

    ......etc. 利用者が専門知識を持たずとも便利に暮らせるのは インターフェースが適切に設計されているから 身近な抽象化、インターフェース