<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ruben Rangel</title>
    <description>The latest articles on DEV Community by Ruben Rangel (@rubenrangel).</description>
    <link>https://dev.to/rubenrangel</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1485448%2Fbf6fd8cf-7afb-4ac6-877e-f1d4cbacc8db.jpeg</url>
      <title>DEV Community: Ruben Rangel</title>
      <link>https://dev.to/rubenrangel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rubenrangel"/>
    <language>en</language>
    <item>
      <title>What I Learned from Domain Modeling in a Team</title>
      <dc:creator>Ruben Rangel</dc:creator>
      <pubDate>Sun, 02 Jun 2024 23:10:40 +0000</pubDate>
      <link>https://dev.to/rubenrangel/what-i-learned-from-domain-modeling-in-a-team-34b4</link>
      <guid>https://dev.to/rubenrangel/what-i-learned-from-domain-modeling-in-a-team-34b4</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In my division at Guild, we've started a working group to develop and iterate on a new domain model, aiming to create the ideal topography for the next evolution of our space. The process incorporates zero-start thinking (starting from a blank slate) and considers inputs such as what product features we want to support now and in the future, how to design the domain to easily adapt to new requirements, and how to align the domain to maximize ownership and the flow of work. The spirit of the exercise is rooted in Domain-Driven Design. Using this model, we aim to provide a North Star of sorts to guide design decisions, trade-off discussions, investments, and what directionally correct choices look like.&lt;/p&gt;

&lt;p&gt;The group consists of leaders in the technology and product organizations. I'm excited to work with them on this large project and learn from them. I work closely alongside the Software Architect in the group, helping to author the model using feedback from stakeholders and our own knowledge and understanding of the domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Communicating with Stakeholders
&lt;/h2&gt;

&lt;p&gt;With a task as complex as designing a domain, communicating with others is crucial. Failure to do so dashes any chances of achieving the project's goals. Nobody truly has the "whole picture" in their heads. Getting other people's perspectives on the domain, listening to their concerns with the model, teasing out information by asking thought-provoking questions, and gathering requirements are just some examples of the process of effective communication. If you can do this with people in various roles (especially in Product and Engineering), you increase your chances of success. Just be sure not to get stuck in analysis paralysis.&lt;/p&gt;

&lt;p&gt;In our group, the revisions of the model have happened in various ways. We've done some small iterations in synchronous meetings, I've facilitated one-on-one sessions with stakeholders, and we've also collaborated asynchronously using Miro. &lt;strong&gt;Getting feedback early and often helps reduce the chances of creating something that doesn't fit our needs.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Making Brave Decisions
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Essentially, all models are wrong, but some are useful.&lt;/p&gt;

&lt;p&gt;- George E. P. Box&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As my career has progressed, I've taken on more responsibility and decision-making, which can be uncomfortable. I often wonder, "Is this decision the right one?" During this domain modeling exercise, I create concepts based on my understanding of different contexts, and that question frequently arises.&lt;/p&gt;

&lt;p&gt;In a conceptual model like the one we're building, where the time horizon is at least a year out if the model had perfect clarity, the answer may not be known for a long time. Even then, by the time we get there, the definition of correct may be entirely different from what it was before. My VP of Engineering gave me great advice though: &lt;strong&gt;"Make a decision and get feedback on it. If it doesn't work, we've eliminated one incorrect option."&lt;/strong&gt; This made me realize that though I may be drawing some boxes in the model, the overall decision and direction belong to the team, not a single person. I shouldn't be hung up on being right. The point of the model is to help inform and provide context, not to be 100% accurate in all regards. Modeling is cheap, and we should be comfortable throwing away ideas now, when it's easy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Balancing Ideal vs. Current State
&lt;/h2&gt;

&lt;p&gt;As I stated earlier, the modeling exercise uses some of the current functionality we want to support as an input. Another is future functionality we want to support. It's hard to keep the former from influencing your thinking too much. These are things we've built; it's easy to reason about and understand them since they're real and these concepts already exist. Striking the balance and allowing ourselves to question everything is key to envisioning what could be. Yes, sometimes we keep some domain objects from the current state around because they make sense, but other times, we take the scary step of not including those objects in the new world. &lt;strong&gt;Envisioning what could be is the goal, and we should use the past to inform the future, but not couple them.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Ever-Evolving Model
&lt;/h2&gt;

&lt;p&gt;I've never been part of a project where the final delivery turns out exactly like the initial design. The completeness of the model is not 100%, and the time horizon for any kind of implementation is long-term. Revisiting and evolving the model is necessary to keep it relevant and useful. Contexts will shift over time: new features will be necessary to accomplish the company's goals, domains may become deprecated, and we'll keep gaining understanding of our product's sector.&lt;/p&gt;

&lt;p&gt;After the initial model is built, one of the follow-on efforts will be to see how we can get from point A to point B and do that in an iterative fashion. Think of things like product release milestones or an architecture modification. &lt;strong&gt;We should use these checkpoints to pause and revisit the model.&lt;/strong&gt; We will have certainly learned more along the way, and building that understanding back into the model will adjust it to be more "correct" than it was. I've seen many diagrams and models that are made once and never updated again, reducing their usefulness and potentially leading to suboptimal decision-making.&lt;/p&gt;

&lt;h2&gt;
  
  
  Continuous Communication and Adaptation
&lt;/h2&gt;

&lt;p&gt;Even though the modeling is done by a working group, the outcomes affect a larger set of people and the systems they work on. Shopping around the model, setting up its context, and getting people's thoughts on it are important for gaining buy-in on the effort. In an organizational culture that emphasizes ownership, "throwing things over the wall" is a mistake. I personally am still learning how to do this well. Who do I share information with, and at what time? How do I make sure their input is heard but also not distract them from their current priorities?&lt;/p&gt;

&lt;p&gt;I don't think there's a one-size-fits-all answer for this. &lt;strong&gt;Generally speaking though, I think it's hard to over-communicate, especially in things that are high in scope.&lt;/strong&gt; There's probably nuance here with the particular people you work with and organizational structures. That pushes more importance on staying aware of how communication flows within the organization and doing what you can to ensure everyone is on the same page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In conclusion, the domain modeling project at Guild has underscored the importance of clear communication, decisive action, and the ability to adapt to changing conditions. This work has taught me to balance current realities with our future vision, continuously refine our models, and engage our entire team in the evolution process.&lt;/p&gt;

&lt;p&gt;Have you been part of large-scale domain exercises like this? What strategies did you employ to achieve your goals?&lt;/p&gt;

</description>
      <category>stakeholdercommunication</category>
      <category>domaindrivendesign</category>
      <category>architecture</category>
      <category>leadership</category>
    </item>
    <item>
      <title>Crafting a Unified GraphQL Experience with AWS AppSync Merged APIs</title>
      <dc:creator>Ruben Rangel</dc:creator>
      <pubDate>Tue, 07 May 2024 04:24:08 +0000</pubDate>
      <link>https://dev.to/rubenrangel/crafting-a-unified-graphql-experience-with-aws-appsync-merged-apis-371h</link>
      <guid>https://dev.to/rubenrangel/crafting-a-unified-graphql-experience-with-aws-appsync-merged-apis-371h</guid>
      <description>&lt;p&gt;Lately, Ive been exploring &lt;a href="https://docs.aws.amazon.com/appsync/latest/devguide/merged-api.html"&gt;AWS AppSync Merged APIs&lt;/a&gt; to better understand the complexities and tradeoffs involved in creating a unified GraphQL API experience that can be supported by various subdomain teams.&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;GraphQL is all about the graph formed by the Types in the schema. Forming relationships between Types allows clients to connect these different entities. This enables enhanced client experiences and can result in more efficient server interactions.&lt;/p&gt;

&lt;p&gt;However, as the number of Types (vertices in the graph) increases, so does the maintenance of the API. Maybe the API holds all the functionality for a scaling business, and so the capabilities of the API and the number of engineers supporting the API grow. If the GraphQL API is implemented in AppSync, Merged APIs can help lower the cognitive load of maintenance by allowing subsections of the API to be powered by Source APIs (and possibly a single domain team).&lt;/p&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;Our simplified example involves the API of a fictional car manufacturer called Wheely. Wheely has software teams supporting two areas of the business: Parts Manufacturing and Car Assembly. Due to various reasons, the two teams prefer not to maintain their shared AppSync API in a single codebase (possibly due to differences like spaces vs. tabs). They decide to split the codebase to better suit their domains' needs but still aim to collectively provide support for a unified GraphQL API to maximize its benefits for the business.&lt;/p&gt;

&lt;p&gt;They opt to use AppSync Merged APIs to create a single Wheely API supported by a single AppSync API for each domain. The Car Assembly team wants to manage the core information about Wheely's car offerings, while the Parts Manufacturing team focuses on overseeing the inventory of parts used in the cars. Both teams also see the value in being able to retrieve all parts used in a specific car as a key feature of the API.&lt;/p&gt;

&lt;h3&gt;
  
  
  API Design
&lt;/h3&gt;

&lt;p&gt;Both source APIs will provide their parts of the overall GraphQL API and each leverage &lt;a href="https://docs.aws.amazon.com/appsync/latest/devguide/merged-api.html#merged-api-conflict-resolution"&gt;the &lt;code&gt;@canonical&lt;/code&gt; annotation&lt;/a&gt; to avoid conflicts on Types and Fields.&lt;/p&gt;

&lt;h4&gt;
  
  
  Car Assembly
&lt;/h4&gt;

&lt;p&gt;The Car Assembly API owns the &lt;code&gt;Car&lt;/code&gt; Type and the &lt;code&gt;getCar&lt;/code&gt; Query. This schema also has the &lt;code&gt;Car.parts&lt;/code&gt; Field, but makes note to other engineers that it does not own it, so other engineers can be aware of this dependency. The same is true for the &lt;code&gt;Part&lt;/code&gt; Type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# Owned by the Parts Manufacturing AppSync API&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Part&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;canonical&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c"&gt;# Owned by the Parts Manufacturing AppSync API&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Part&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;getCar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;canonical&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Parts Manufacturing
&lt;/h4&gt;

&lt;p&gt;The Parts Manufacturing API owns the &lt;code&gt;Part&lt;/code&gt; Type and the &lt;code&gt;getPart&lt;/code&gt; Field. It also owns the &lt;code&gt;Car.parts&lt;/code&gt; Field even though it doesn't own the &lt;code&gt;Car&lt;/code&gt; Type. This AppSync API has a resolver for the &lt;code&gt;Car.parts&lt;/code&gt; Field and no other source API can add a resolver for that Field, making this API the source of truth for this piece of data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# Owned by the Car Assembly AppSync API&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Part&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;canonical&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Part&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;canonical&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;getPart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Part&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;canonical&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  &lt;code&gt;Car.parts&lt;/code&gt; Resolver
&lt;/h5&gt;

&lt;p&gt;This resolver relies on the IDs of the parts to retrieve being passed to it either via the &lt;code&gt;source&lt;/code&gt; or &lt;code&gt;stash&lt;/code&gt; in the context. It then uses those IDs to filter its datastore to retrieve the parts needed to fulfill the &lt;code&gt;parts&lt;/code&gt; Field for the request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * This resolver assumes the part IDs will be a field from the parent field OR be included in the stash.
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/**
     * @type {string[] | undefined}
     */&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;partIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;partIds&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;partIds&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;partIds&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;partIds&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No part IDs supplied.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Headlights&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sunroof&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Leather Seats&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cloth Seats&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;part&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;partIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Merged API
&lt;/h4&gt;

&lt;p&gt;This is the final merged API that the client would depend on. The client doesn't see that the implementation of this is spread across multiple APIs. We've really leaned into the "Interface" part of the API here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Part&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Part&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;getCar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;getPart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Part&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tradeoffs and Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Managing API Design
&lt;/h3&gt;

&lt;p&gt;With the nested Type dependency from the merged APIs, communication between owners of the Source APIs is important. GraphQL design (and any API for that matter) should have time put into the "how" it should work. Doing that over disparate teams and evolving the API over time increases complexity.&lt;/p&gt;

&lt;p&gt;Collaboration is always important in software engineering. However, using Merged APIs without a plan of how to drive changes, who owns which parts, or which parts live in which AppSync APIs could hinder success.&lt;/p&gt;

&lt;h3&gt;
  
  
  Contract Coordination
&lt;/h3&gt;

&lt;p&gt;As you can see in the &lt;code&gt;Car.parts&lt;/code&gt; resolver, it expects the &lt;code&gt;partIds&lt;/code&gt; data to be passed in either from the parent Field resolver (in the &lt;code&gt;source&lt;/code&gt;) or inserted earlier in the request's &lt;code&gt;stash&lt;/code&gt;. The implementation in the repository will have the &lt;code&gt;Query.getCar&lt;/code&gt; resolver always return the &lt;code&gt;partIds&lt;/code&gt; Field to AppSync, even if the client doesn't request the &lt;code&gt;Car.parts&lt;/code&gt; Field. It's returning the entire data model.&lt;/p&gt;

&lt;p&gt;This works fine for this simple example. But, maybe in the future, the Car Assembly team makes a design decision to only return to AppSync Fields that the client requests, so they can increase performance. If the client doesn't request &lt;code&gt;Car.parts&lt;/code&gt;, then the resolver will not return &lt;code&gt;partIds&lt;/code&gt;, and then the implicit contract between that API and the Parts Manufacturing API will break.&lt;/p&gt;

&lt;p&gt;Similar to the API design consideration above, the software teams need to be aware of the interactions between dependencies and dependents.&lt;/p&gt;

&lt;h3&gt;
  
  
  "Unowned" API Resources
&lt;/h3&gt;

&lt;p&gt;In AppSync Merged APIs, the resulting API is the combination of all the source APIs. Not everything in the Merged API needs the &lt;code&gt;@canonical&lt;/code&gt; annotation though. Source APIs can contribute any Type, Query, Mutation, etc. and not provide any conflict-resolution through the &lt;code&gt;@canonical&lt;/code&gt; annotation. Conflicting definitions could result in unexpected behaviors for clients and bugs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/rubenrangel/appsync-merged-api-cars"&gt;Demo repo (rubenrangel/appsync-merged-api-cars)&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/appsync/latest/devguide/merged-api.html"&gt;AppSync Merged APIs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.apollographql.com/tutorials/lift-off-part3/05-resolver-chains"&gt;Apollo GraphQL resolver chaining overview&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>appsync</category>
      <category>graphql</category>
      <category>serverless</category>
    </item>
  </channel>
</rss>
