architecture 2024-10-17

i can't for the life of me remember the name of this pattern where you have a type with Foo and to augment, you create FooWithX and FooWithY. i am trying to dissuade others from going this route with graphql schema modeling since it seems like such a nightmare over the long term and gives you all these types to talk about the same entity.

Martynas Maciulevičius 2024-11-23T09:54:46.922699Z

you create FooWithX and FooWithY. i am trying to dissuade others from going this route
Try to propose a type Foo_WithX_WithY_WithoutFoo and they might calm down (added underscores for dramatic effect) Also you might want to think if now you have two Y in one object (`FooWithYWithSecondYWithNthY`). I think this would be the real counter for not denormalizing your entity.

Not sure if this is of interest, but your initial message reminding me of Mixins. Sure enough, searching for mixin support in graphql led to a new rabbit hole which can surely only lead to sadness and despair …. https://github.com/graphql/graphql-spec/issues/370

Decorator pattern, maybe?

Could be Decorator if using composition. Or could just be standard inheritance if using well inheritance.

thanks, that seems in the ballpark. the graphql data model looks like this

type FooWithX {
  id: ID!
  foo: Foo!
  x: int
}

Ya, that looks like Decorator using Composition

You have a new type composed of another + additional things.

Typically, decorator exposes the same methods as what it decorates and extends the same base type/interface, and it delegates to the instance of the decorated type for those methods, but then it can also add more methods or behavior before/after each method call.

But here, it's purely a data type, with no methods. So maybe this is just Composition.

right. i think it will become chaos when someone else needs to augment this type and we end up with Foo, FooWithX, FooWithY, all with different ids in the system even though they refer to the same business object

Hum, it depends. It could be a form of variant, or tagged union. That's more a typed FP pattern. It's a bit different in that, you're saying that Foo is a type with various "cases" called variants. I've seen it used for "User" often. Where a User exists in many variations. Before signing up you might have a UnregisteredUser, once signed up it's a RegisteredUser, when logged in it's a LoggedInUser, etc.

But, in a variant record, you don't compose the original type Foo, you just list all fields of each variant.

Here's a Clojure example: [:point-2d, 23, 56] [:point-3d, 23, 56, 10] [:point-4d, 76, 23, 45, 29] You could say that Point is a variant record where possible variations are :point-2d, :point-3d and :point-4d If you did a Clojure Spec for it, you would say: (s/def ::point (s/or ...)) Where you'd have or a vector of 3 elements where first element is tagged as :point-2d etc.

So Point is one-of those variants. And a tag distinguishes them, letting you condition the behavior based on which variant you have.

I don't know if GraphQL schema can even define those kind of types though 😅

It's not really that you create 4 different types, normally you'd define a single type Point and specify that it is one of many variant.

right, i think this is a limitation of graphql data modeling. it also doesn't have namespaced keys (😢) which i think would handle this problem more gracefully. the underlying issue is how to let teams enrich types controller by other teams in a graphql world.

I think you'd use Union in GraphQL like:

union Point = Point2D | Point3D | Point4D

type Point2D {
  x: Float!
  y: Float!
}

type Point3D {
  x: Float!
  y: Float!
  z: Float!
}

type Point4D {
  x: Float!
  y: Float!
  z: Float!
  w: Float!
}

type Query {
  getPoint: Point
}

Ah ya, you want extension of types controlled by other teams. Sorry 😔. Don't think GraphQL has a clean way for that

It's a downside of these closed type/schema systems

yes i think that must be the friction i'm feeling

maybe i can get everyone to watch the maybe not talk lol