Last time I wrote about modeling a custom physical product as a JSON spec. Everything in that schema quietly assumed a rectangular object: width, height, depth, a tolerance, and corners.
Then a real product arrived that broke all of it — an egg-shaped wine case. No corners, no flat faces, and no obvious "width".
Here's what a curved product exposes about a data model, and how to handle it.
Where the rectangular assumption breaks
The original type was:
type Dimensions = { widthMm: number; heightMm: number; depthMm: number };
For a box, that is complete. For an oval case:
- width/height/depth still describe the bounding envelope, but not the form
- the front is a curved surface with a cut-out aperture
- the interior is profiled to the bottle's shoulder, not a rectangle
- tolerance applies to a curve, not to an edge
The moment you need to quote tooling for this, the schema is visibly under-specified.
The fix: separate envelope from profile
Keep the bounding box — it is still useful for volume and shipping — and add a profile describing the actual cross-section:
type Profile =
| { kind: 'rect'; cornerRadiusMm?: number }
| { kind: 'ellipse'; rxMm: number; ryMm: number }
| { kind: 'bezier'; controlPoints: readonly [number, number][] };
type Dimensions = {
envelope: { widthMm: number; heightMm: number; depthMm: number };
profile: Profile;
toleranceMm: number;
};
Now "oval" is data, not a sentence in an email thread.
Three fields a curved case adds
type CurvedCaseExtras = {
orientation: 'upright' | 'lying';
aperture?: {
shape: 'ellipse';
rxMm: number;
ryMm: number;
frameWidthMm: number;
};
interiorProfile: 'flat' | 'profiled';
};
Why each one exists:
- orientation — a standing oval has an "up" axis. It changes how the case is tooled and how it sits in a shipping carton.
- aperture — the window cut into the face. For a plain box this is zero. Here it is a core design element, and it creates a fabrication step of its own.
- interiorProfile — the inner cutout follows the product's contour. "Does the bottle fit" stops being a boolean and becomes a curve-fit question.
The general lesson
Physical products do not fit a schema designed for cardboard boxes. The fix is not a bigger schema — it is separating what is universal from what is shape-specific:
- Universal: quantity, tolerance, finish, branding, lead time
- Shape-specific: envelope + profile + aperture + interior
You get the universal part right once. The shape-specific part you extend per product family, instead of bolting on fields until the model collapses.
Takeaway
If your product data is a width/height/depth triple and nothing else, it works fine — until the first non-rectangular product arrives. Add a profile before that happens, and keep the envelope for everything that genuinely only cares about size.
The curved case in this article is a real product from TANCYBOX, a packaging manufacturer — the schema gaps came from actually having to quote it.


Top comments (0)