
Soul Global 2.0
A full architectural rewrite of Soul Global β from a React + REST monolith to Next.js 16 Server Components with durable workflows.
Version 1 proved the product but accumulated client-side complexity and a brittle order model. Version 2 is a ground-up rewrite on a server-component-first architecture, with Inngest workflows and a complete Brazilian import tax engine. The hard part was migrating live production data while v1 kept serving paying customers.
ROLE
Software Engineer
Full Stack
Architect
TOOLS
Bun
Next.js 16
React 19
PostgreSQL
Drizzle ORM
Inngest
Decimal.js
next-intl
DURATION
2025 - Present
INTEGRATIONS
Supabase
Siscomex (Portal Γnico)
Asaas
ZapSign
ShipsGo
Anthropic Claude
π
soulglobal.com.br
βΆ The system, end to end
Four personas hit one App Router, which talks to a service layer, a pure pricing engine, 55 Postgres tables and eight external systems β while inbound webhooks feed 18 durable Inngest functions. Tap any node to open it.
βΆ Live data migration (11-step β 7-step)
Migrated the legacy schema to a simpler order model while preserving every historical order, contract, and payment β as two versioned pipelines of idempotent phases, with the old-to-new id map kept in a real table so a crash never means starting over.
Legacy migration as a re-runnable pipeline
migrate-legacy Β· 8 phases
migrate-v2 Β· 6 phases
_migration_id_mapping
legacy id
β
new uuid
primary key (entity_type, old_id) β a real table, not an in-memory Map, so the mapping survives a crash and a restart resumes where it stopped
βΆ Client-fetching β React Server Components
Re-architected to Next.js 16 Server Components by default, dropping initial load weight and reworking auth and caching that were core to v1's client-side SWR model.
βΆ Durable workflows with Inngest
Replaced manual state machines and fragile cron jobs with 18 Inngest functions β serialised one-per-shipment, retried three times, idempotent by design, so payment reconciliation and customs progression survive crashes and 3rd-party outages.
Inbound webhook β durable workflow
webhook
verify + look up local row
inngest.send(event)
shipment-step-evaluator
concurrency: { key: shipmentId, limit: 1 }
retries: 3 Β· idempotent guarded UPDATE
step.sleepUntil(eta) β no cron polling
postgres tx
in-app notify
shipment state machine
βΆ Brazilian import tax engine
Full CIF β II β IPI β PIS/COFINS β Siscomex β AFRMM β ICMS cascade, including the "por dentro" gross-up, computed with Decimal.js so rounding never drifts.
Import tax cascade β one line item
CIF
100.000,00
capatazia / THC stay out of the base β Decree 11.090
II Β· 16%
16.000,00
IPI Β· 9,75%
11.310,00
the IPI base already carries the II
PIS Β· 2,10%
2.100,00
no ICMS in the base β STF, RE 559.937
COFINS Β· 9,65%
9.650,00
Siscomex
154,23
grouped by NCM + manufacturer, not per line item
AFRMM
960,00
ICMS base
140.174,23
ICMS Β· 18% por dentro
30.769,95
the tax sits inside its own base β a gross-up, not a flat 18%
landed cost
170.944,18
A naive ICMS (base Γ 18%) returns 25.231,36 β off by 5.538,59 on a single item, then multiplied across every line of every invoice.
βΆ Four personas on one database
Importers, Chinese sourcing partners, customs brokers and platform staff each get their own route group, gated in the layout before any child renders and backed by a signed active-organization cookie.
Four personas, one database
(admin)
platform staff
systemRole = SUPER_ADMIN
| SUPER_ADMIN_EMPLOYEE
every organization, rate tables, audit logs
(dashboard)
importer
signed org cookie
+ membership row
own quotes, shipments, invoices and documents
(china)
sourcing partner
β¦all of the above
+ isChinaPartner
only sourcing requests assigned to that partner
(broker)
customs broker
β¦all of the above
+ broker org
shipments where it is the customs broker, at customs stage
active org resolution
cookie active_organization_id
+ active_organization_sig β hmac-sha256, constant-time compare
tampered or stale β redirect /api/clear-org
one shipments row, three organization roles
shipments