Arquitectura
Como esta organizado el codigo y por que cambiar de proveedor no obliga a reescribir el producto.
ApisKit usa arquitectura hexagonal, tambien llamada puertos y adaptadores. En una frase: el corazon de tu negocio no sabe que existe Firebase, ni Stripe, ni Next.js. Sabe que existe un contrato, y alguien lo cumple.
Eso no es purismo academico. Es lo que hace que cambiar de proveedor sea escribir un fichero nuevo en vez de reescribir el producto.
Las capas
src/
core/ el dominio, sin nada de fuera
entities/ objetos del negocio (User, SupportTicket, Trial,
CreditPurchase, Payment, DocPage)
ports/ contratos que el exterior debe cumplir
(AuthPort, UserPort, PaymentPort, StoragePort, DocsPort)
adapters/ quien cumple esos contratos de verdad
database/ Firebase y Firestore (y uno en memoria, para las pruebas)
payment/ Stripe
app/ rutas, API y paginas de Next.js: aqui se enchufa todo
presentation/ componentes de React
lib/ utilidades transversales (sesion, correo, errores, SEO,
limites de peticiones, pintado de documentacion)
config/ product.ts (identidad), idiomas, tema, firebase, precios
locales/ traducciones es/ y en/
La regla de dependencia
Las dependencias apuntan hacia dentro, nunca hacia fuera:
core/no importa nada del exterior. Ni una libreria. Define interfaces.adapters/conocen acorey a los SDK de fuera, y los conectan. Los proveedores viven aqui y en ningun otro sitio.app/ypresentation/usancorey los adaptadores, nunca al reves.
Si algun dia buscas "stripe" en src/core/, no debe aparecer. El dia que
aparezca, la ventaja se ha perdido.
Un puerto tambien se parte por permisos
El uso normal de un puerto es cambiar de proveedor. Hay otro que se aprovecha menos y evita fallos silenciosos: partirlo segun lo que cada llamante tiene permitido hacer.
DocsPort son dos interfaces, no una:
DocsReadPort, que solo sabe leer paginas publicadas.DocsAdminPort, que lo puede todo, borradores incluidos.
A la pagina publica se le entrega el puerto de lectura. Asi no es que no deba pedir un borrador: es que no tiene ningun metodo para pedirlo. La fuga no se evita con disciplina ni con una revision, se evita porque no se puede escribir.
Usa el mismo truco donde una fuga seria silenciosa.
La consecuencia practica
Cambiar de base de datos o de pasarela de pago es escribir un adaptador nuevo contra el mismo puerto y cambiar donde se enchufa. Tus reglas de negocio no se tocan.
Es tambien lo que permite que piezas enteras, como el sistema de soporte, se puedan sacar y llevar a otra aplicacion: hablan con el resto solo a traves de un contrato pequeno y escrito.
Identidad y configuracion
src/config/product.ts es la fuente unica de la identidad: nombre, empresa,
direcciones, correo de soporte. Se valida con Zod al cargar el modulo, asi
que una configuracion mal puesta falla al arrancar en vez de desplegarse rota.
De ahi beben el SEO, los correos, los datos estructurados, la cabecera, el pie y los textos traducidos. Por eso se re-marca con un comando: solo hay un sitio que cambiar.
Los dos idiomas
src/config/i18n da el proveedor y la funcion t(). Las traducciones estan en
src/locales/{es,en}/<espacio>.json. Los distintivos de identidad ({brand},
{company}, {supportEmail}, {baseUrl}) se inyectan en cada texto, para que
nadie escriba el nombre de la marca a mano.
npm run verify:i18n comprueba que espanol e ingles son simetricos y que cada
t() apunta a una clave que existe.
Donde va tu producto
Tu dominio son entidades y puertos nuevos en core/, con sus adaptadores en
adapters/, expuestos por rutas en app/ y componentes en presentation/.
Si adaptar tu producto te obliga a cambiar codigo que ya estaba en core/,
tratalo como un fallo de la plantilla y arreglalo ahi, no como un parche para tu
caso. Esa es la diferencia entre una base que aguanta y una que se pudre.