O slideshow foi denunciado.
Seu SlideShare está sendo baixado. ×

SymfonyCon - Dilemmas and decisions..pdf

Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio

Confira estes a seguir

1 de 95 Anúncio

SymfonyCon - Dilemmas and decisions..pdf

Baixar para ler offline

Sylius and API Platform integration is the project that we started in early 2020. Since then, we were working hard designing, delivering and adjusting our new API. We've learned a lot and during my presentation, I would like to share our findings.

Why did we design our API this way?
What were our heuristics and what did we achieve?
How to design more complicated flows in API?

Sylius and API Platform integration is the project that we started in early 2020. Since then, we were working hard designing, delivering and adjusting our new API. We've learned a lot and during my presentation, I would like to share our findings.

Why did we design our API this way?
What were our heuristics and what did we achieve?
How to design more complicated flows in API?

Anúncio
Anúncio

Mais Conteúdo rRelacionado

Mais recentes (20)

Anúncio

SymfonyCon - Dilemmas and decisions..pdf

  1. 1. Dilemmas and decisions What we’ve learned designing the new Sylius API
  2. 2. Introduction 01 Photo by Mikhail Vasilyev on Unsplash
  3. 3. sylius.com 3
  4. 4. Certi fi cation 🎄
  5. 5. SYLIUSROCKS -30%
  6. 6. Paid OS Support
  7. 7. Modular SyliusPlus
  8. 8. sylius.com 8
  9. 9. sylius.com 9 Started in 2020 1.12 with AP 2.7 (since 31st of Oct) 1.13 stabilized with AP 3.0 🎉 ~100% of shop & ~70% of all coverage
  10. 10. Photo by Shane Aldendorff on Unsplash Decisions & consequences 02
  11. 11. Strategic design
  12. 12. sylius.com 12 ADRs
  13. 13. [short title of solved problem and solution] Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005] Date: [YYYY-MM-DD when the decision was last updated] Context and Problem Statement [Describe the context and problem statement] Decision Drivers [driver 1, e.g., a force, facing concern, …] … Considered Options [option 1] [example | description | pointer to more information | …] Good, because [argument a] Bad, because [argument b] … Decision Outcome Chosen option: "[option 1]", because [justi fi cation]. References [Link type] [Link to ADR] …
  14. 14. [short title of solved problem and solution] Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005] Date: [YYYY-MM-DD when the decision was last updated] Context and Problem Statement [Describe the context and problem statement] Decision Drivers [driver 1, e.g., a force, facing concern, …] … Considered Options [option 1] [example | description | pointer to more information | …] Good, because [argument a] Bad, because [argument b] … Decision Outcome Chosen option: "[option 1]", because [justi fi cation]. References [Link type] [Link to ADR] …
  15. 15. [short title of solved problem and solution] Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005] Date: [YYYY-MM-DD when the decision was last updated] Context and Problem Statement [Describe the context and problem statement] Decision Drivers [driver 1, e.g., a force, facing concern, …] … Considered Options [option 1] [example | description | pointer to more information | …] Good, because [argument a] Bad, because [argument b] … Decision Outcome Chosen option: "[option 1]", because [justi fi cation]. References [Link type] [Link to ADR] …
  16. 16. [short title of solved problem and solution] Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005] Date: [YYYY-MM-DD when the decision was last updated] Context and Problem Statement [Describe the context and problem statement] Decision Drivers [driver 1, e.g., a force, facing concern, …] … Considered Options [option 1] [example | description | pointer to more information | …] Good, because [argument a] Bad, because [argument b] … Decision Outcome Chosen option: "[option 1]", because [justi fi cation]. References [Link type] [Link to ADR] …
  17. 17. [short title of solved problem and solution] Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005] Date: [YYYY-MM-DD when the decision was last updated] Context and Problem Statement [Describe the context and problem statement] Decision Drivers [driver 1, e.g., a force, facing concern, …] … Considered Options [option 1] [example | description | pointer to more information | …] Good, because [argument a] Bad, because [argument b] … Decision Outcome Chosen option: "[option 1]", because [justi fi cation]. References [Link type] [Link to ADR] …
  18. 18. [short title of solved problem and solution] Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005] Date: [YYYY-MM-DD when the decision was last updated] Context and Problem Statement [Describe the context and problem statement] Decision Drivers [driver 1, e.g., a force, facing concern, …] … Considered Options [option 1] [example | description | pointer to more information | …] Good, because [argument a] Bad, because [argument b] … Decision Outcome Chosen option: "[option 1]", because [justi fi cation]. References [Link type] [Link to ADR] …
  19. 19. [short title of solved problem and solution] Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005] Date: [YYYY-MM-DD when the decision was last updated] Context and Problem Statement [Describe the context and problem statement] Decision Drivers [driver 1, e.g., a force, facing concern, …] … Considered Options [option 1] [example | description | pointer to more information | …] Good, because [argument a] Bad, because [argument b] … Decision Outcome Chosen option: "[option 1]", because [justi fi cation]. References [Link type] [Link to ADR] …
  20. 20. [short title of solved problem and solution] Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005] Date: [YYYY-MM-DD when the decision was last updated] Context and Problem Statement [Describe the context and problem statement] Decision Drivers [driver 1, e.g., a force, facing concern, …] … Considered Options [option 1] [example | description | pointer to more information | …] Good, because [argument a] Bad, because [argument b] … Decision Outcome Chosen option: "[option 1]", because [justi fi cation]. References [Link type] [Link to ADR] …
  21. 21. sylius.com 21 Conclusion?
  22. 22. sylius.com 22 GraphQL vs REST
  23. 23. 2020 -> GraphQL 🚀
  24. 24. Is it still?
  25. 25. Is it not?
  26. 26. sylius.com 28 ✔ Solves over fetching and under fetching By design ✔ Typed, nice documentation ? Sends everything with POST It is possible to do it with GET ✔ Gracefully deprecation of queries Which was not possible with default REST GraphQL
  27. 27. sylius.com 29 ✔ May solve over fetching and under fetching With spare fi elds sets and/or Vulcain ✔ Typed, nice documentation With OpenAPI ✔ Takes advantage of 20 years of web cache development Fake date institute ™ ✔ Gracefully deprecation of queries With OpenAPI REST
  28. 28. sylius.com 30 Conclusion?
  29. 29. Resources design
  30. 30. sylius.com 32 State transitions
  31. 31. Case Let’s cancel an order!
  32. 32. sylius.com 34 Considered option #1 PATCH /api/orders/42/ { "state": "cancelled" }
  33. 33. sylius.com 35 Considered option #2 PATCH /api/orders/42/cancel {}
  34. 34. RESTful Archetypes Document /api/admin/orders/1 Store Client controlled /api/admin/orders/123 Collections Server controlled /api/admin/orders Controller /api/admin/orders/1/cancel Based on: REST API Design Rulebook by Mark Masse
  35. 35. sylius.com 38 Isn’t there a better way?
  36. 36. sylius.com 39 Solution? POST /api/orders-cancellation-requests/ {}
  37. 37. sylius.com 40 Conclusion?
  38. 38. sylius.com 41 Calculated data
  39. 39. Case Cost of the shipment
  40. 40. Version #1 - Adding fi elds on entity class ShippingMethod { /** rest of methods */ public ?int $cost; // never used in app // serialized only when possible to count }
  41. 41. Version #2 - Read model class CartShippingMethod { public function __construct( public readonly string $code, public readonly ShippingMethodInterface $shippingMethod, public readonly int $cost ) { } }
  42. 42. Version #3 - Dynamic fi eld
  43. 43. public function normalize($object, $format = null, array $context = []) { Assert::keyNotExists($context, self::ALREADY_CALLED); $context[self::ALREADY_CALLED] = true; $data = $this->normalizer->normalize($object, $format, $context); $calculator = $this->shippingCalculators->get($object->getCalculator()); $data['price'] = $calculator->calculate( $shipment, $object->getConfiguration() ); return $data; } public function normalize($object, $format = null, array $context = []) { Assert::keyNotExists($context, self::ALREADY_CALLED); $context[self::ALREADY_CALLED] = true; $data = $this->normalizer->normalize($object, $format, $context); $calculator = $this->shippingCalculators->get($object->getCalculator()); $data['price'] = $calculator->calculate( $shipment, $object->getConfiguration() ); return $data; } public function normalize($object, $format = null, array $context = []) { Assert::keyNotExists($context, self::ALREADY_CALLED); $context[self::ALREADY_CALLED] = true; $data = $this->normalizer->normalize($object, $format, $context); $calculator = $this->shippingCalculators->get($object->getCalculator()); $data['price'] = $calculator->calculate( $shipment, $object->getConfiguration() ); return $data; } public function normalize($object, $format = null, array $context = []) { Assert::keyNotExists($context, self::ALREADY_CALLED); $context[self::ALREADY_CALLED] = true; $data = $this->normalizer->normalize($object, $format, $context); $calculator = $this->shippingCalculators->get($object->getCalculator()); $data['price'] = $calculator->calculate( $shipment, $object->getConfiguration() ); return $data; } Version #3 - Dynamic fi eld
  44. 44. sylius.com 47 Conclusion?
  45. 45. High level API design
  46. 46. sylius.com 49 Uni fi cation of API
  47. 47. /api/products/ Admin & Shop served together
  48. 48. sylius.com 51 ✘ Available fi elds Complicated serialisation groups depending on logged in user ✘ Hard to de fi ne identi fi ers We have resigned from them later ✘ Requirement to de fi ne granular access control To now allow to access sensitive date for non-admins Findings
  49. 49. sylius.com 52 🛒 Shop has 72 endpoints 64% of read endpoints 🖊 Only 20% of resources have writable capabilities in shop 40% of them are never exposed in shop ⚙ Admin has 128 endpoints 52% of read endpoints Data
  50. 50. Option #1 - Admin & Shop pre fi xed /api/products/?admin
  51. 51. sylius.com 54 ✔ Available fi elds Depending on logged in user ✘ Seems wrong from the REST perspective If we add pre fi x to the URL ✘ Requirement to de fi ne granular access control To now allow to access sensitive date for non-admins Findings
  52. 52. Option #2 - Admin & Shop header split /api/products/ Accept: application/vnd.sylius-admin.api+json
  53. 53. sylius.com 56 ✔ Available fi elds Depending on logged in user ✔ REST compilant ✘ Requirement to de fi ne granular access control May be mitigated with Voters Findings ✘ Not easily supported By API Platform and Open API spec
  54. 54. Option #3 - Admin & Shop pre fi xed /api/shop/products/ /api/admin/products/
  55. 55. sylius.com 58 ✔ Available fi elds Depending on logged in user ✔/✘ REST compilant Disputable ✔ Straightforward access control Just with security con fi g Findings ✔ Easily supported By API Platform and Open API spec
  56. 56. sylius.com 59 Conclusion?
  57. 57. sylius.com 60 API versioning
  58. 58. /api/v1 Old Admin API
  59. 59. #1 version /new-api/
  60. 60. sylius.com 63 URL based versioning /api/v2 Custom header X-Sylius-API-Version: 1 Accept header with an additional vendor information Accept: application/vnd.sylius.v1+json #2 version
  61. 61. sylius.com 66 Conclusion?
  62. 62. sylius.com 67 But!
  63. 63. sylius.com 68 🔮 Versioning endpoints With sunset header 🔮 Vendor added to accept header application/vnd.sylius.v1+json 🔮 Deprecating endpoints, fi elds etc In documentation of Open API Future
  64. 64. API fl ow design
  65. 65. Case #1 Add to cart
  66. 66. Simple product { "product": “/api/products/42“, "quantity": 1 }
  67. 67. Con fi gurable product #1 { "product": “/api/products/42“, "productVariant": “/api/product-variants/42“, "quantity": 1 }
  68. 68. { "product": “/api/products/42“, “options": { "SIZE": “SIZE_L", "COLOR": “COLOR_BLUE" }, "quantity": 1 } Con fi gurable product #2
  69. 69. { "product": “/api/products/864“, "productVariant": “/api/product-variants/864“, "quantity": 1 } Let’s improve! { "product": “/api/products/42“, "quantity": 1 } Configurable product Simple product
  70. 70. Let’s improve! { "product": “/api/products/864“, "productVariant": “/api/product-variants/864“, "quantity": 1 } { "product": “/api/products/42“, "productVariant": “/api/product-variants/42“, "quantity": 1 } Configurable product Simple product
  71. 71. Let’s improve! { "product": “/api/products/42“, "productVariant": “/api/product-variants/42“, "quantity": 1 } Configurable product Simple product
  72. 72. Let’s improve! { "productVariant": “/api/product-variants/42“, "quantity": 1 } Configurable product Simple product
  73. 73. sylius.com 78 But what with options?
  74. 74. Price matrix in UI or Ask us
  75. 75. sylius.com 80 Conclusion?
  76. 76. Case #2 Order details
  77. 77. Apply coupon
  78. 78. Apply coupon PATCH ​​ /ap i​ /v 2​ /sho p​ /order s​ /TOKEN_VALU E​ /apply-coupon { "couponCode": “CHRISTMAS_SALE" }
  79. 79. Cart claiming & addressing
  80. 80. Cart claiming & addressing PATCH ​ /ap i​ /v 2​ /sho p​ /order s​ /TOKEN_VALUE/address { "email": “test@example.com”, "billingAddress": { "firstName": "Jane", "lastName": "Doe", "...": "..." } }
  81. 81. sylius.com 86 ✔ We are used to this separation Mockups “force” such design ✔ Addressing requires state machine transition While coupon appliance cart processing ✔ Di ff erent data required on di ff erent pages Reasoning?
  82. 82. What about order update?
  83. 83. Order update PUT ​ /ap i​ /v 2​ /sho p​ /order s​ /TOKEN_VALUE { "localeCode": “en_US” }
  84. 84. But it is just order drafting!
  85. 85. sylius.com 90 ✔ UI Mockups should not force any design decision We can preload data from more then one endpoint ✔ Don’t use state machine where there is none ✔ Either store data earlier or use partial update Changed attitude
  86. 86. PUT ​ /ap i​ /v 2​ /sho p​ /order s​ /TOKEN_VALUE/ { "email": “test@example.com”, "billingAddress": { "firstName": "Jane", "lastName": "Doe", "countryCode": "US", "street": “Baker Street 221B”, "city": “London", "postcode": "Doe" }, "couponCode": “CHRISTMAS_SALE" } Order update
  87. 87. sylius.com 92 Conclusion?
  88. 88. Photo by Mikhail Vasilyev on Unsplash 03 Takeaways
  89. 89. sylius.com 94 Use ADRs And browse them from time to time Custom logic? New API resource! Let’s behave like a tax department! REST will be with us for the long time But GraphQL will be there as well Do not map HTML based websites to your API I know, it was obvious 😅
  90. 90. sylius.com 95 Thank you! Photo by Anthony DELANOIX on Unsplash @lukaszchrusciel @lchrusciel@mastodon.social @lchrusciel

×