ASD: El Patrón de Diseño que Probablemente Estás Usando Mal (o No Conoces)
May 1, 2026
ASD (Application-Service-Domain) es un patrón arquitectónico que separa lógica de aplicación, servicios de negocio y dominio. Después de 12 años construyendo sistemas complejos, te explico por qué es crucial para escalar equipos y código sin volverse loco.
ASD: El Patrón de Diseño que Probablemente Estás Usando Mal (o No Conoces)
Después de heredar suficiente código legacy como para tener pesadillas recurrentes, descubrí que la mayoría de los problemas arquitectónicos vienen del mismo lugar: mezclar responsabilidades que no deberían estar juntas.
ASD (Application-Service-Domain) es el patrón que salvó mi cordura en Snapchat cuando teníamos que escalar sistemas conversacionales que procesaban millones de mensajes. No es glamoroso, pero funciona.
Qué es ASD (sin el fluff académico)
ASD separa tu código en tres capas claras:
- Application: Controllers, endpoints, CLI commands. Todo lo que toca el mundo exterior.
- Service: Lógica de negocio, orquestación, casos de uso. El "qué" hace tu sistema.
- Domain: Entidades, reglas de negocio puras, validaciones. El "corazón" inmutable.
La clave está en las dependencias: Application → Service → Domain. Nunca al revés.
Por qué me importa (y debería importarte)
En 84.51°, construíamos pipelines de datos que procesaban terabytes de información de retail. Cuando un controller tiene lógica de negocio, cuando un service conoce detalles HTTP, cuando el dominio depende de tu ORM favorito... todo se convierte en espagueti imposible de testear.
He visto equipos completos bloqueados porque cambiar un endpoint requería tocar 15 archivos. ASD te da separación de preocupaciones real, no la que prometemos en los PRs y nunca cumplimos.
Cómo implementarlo sin sobre-ingeniería
Application Layer
// api/users/route.ts (Next.js 14)
export async function POST(request: Request) {
const body = await request.json();
const result = await UserService.createUser(body);
if (!result.success) {
return Response.json({ error: result.error }, { status: 400 });
}
return Response.json(result.data, { status: 201 });
}
Tu controller solo traduce HTTP a llamadas de servicio. Nada más.
Service Layer
// services/UserService.ts
export class UserService {
static async createUser(data: CreateUserDTO) {
// Orquestación de lógica de negocio
const user = User.create(data); // Domain entity
if (!user.isValid()) {
return { success: false, error: user.errors };
}
await UserRepository.save(user);
await EmailService.sendWelcome(user.email);
await AnalyticsService.track('user_created', user.id);
return { success: true, data: user };
}
}
El service orquesta. Coordina domain entities, repositorios, servicios externos. Es el director de orquesta, no el violinista.
Domain Layer
// domain/User.ts
export class User {
private constructor(
public id: string,
public email: string,
public name: string
) {}
static create(data: CreateUserDTO): User {
// Lógica de negocio pura
const user = new User(
crypto.randomUUID(),
data.email.toLowerCase().trim(),
data.name
);
return user;
}
isValid(): boolean {
return this.email.includes('@') && this.name.length > 0;
}
}
El dominio no sabe nada de bases de datos, HTTP, o frameworks. Es lógica de negocio pura y testeable.
Los errores que veo constantemente
1. Services con lógica de presentación
// ❌ MAL
class UserService {
async getUser(id: string) {
const user = await db.query(...);
return { ...user, fullName: `${user.firstName} ${user.lastName}` };
}
}
Eso es responsabilidad del Application layer. El service devuelve el domain entity, punto.
2. Domain que conoce infraestructura
// ❌ MAL
class User {
async save() {
await supabase.from('users').insert(this);
}
}
Tu dominio debe poder vivir sin saber qué base de datos usas. Supabase, Postgres, MongoDB... no debería importarle.
3. Controllers con lógica de negocio
// ❌ MAL
export async function POST(request: Request) {
const body = await request.json();
if (!body.email.includes('@')) {
return Response.json({ error: 'Invalid email' });
}
// ... más validaciones ...
}
Esa validación es lógica de negocio. Va en el domain.
Cuándo NO usar ASD
Seamos honestos: para un CRUD de 3 tablas que nunca va a crecer, ASD es overkill. Si estás haciendo un MVP de fin de semana, mételo todo en el controller y continúa.
Pero cuando tu equipo crece, cuando necesitas reutilizar lógica en webhooks/APIs/background jobs, cuando quieres testear sin levantar servidores... ahí es donde ASD brilla.
Mi regla de oro
Si no puedes testear tu lógica de negocio sin una base de datos corriendo, algo está mal.
Si cambiar de REST a GraphQL requiere reescribir servicios, algo está mal.
Si agregar un nuevo canal (mobile, web, CLI) significa duplicar código, algo está mal.
ASD no es perfecto, pero después de 12 años, es el patrón que consistentemente me ha permitido escalar código y equipos sin volverme completamente loco.
¿Vale la pena el overhead inicial? Depende. Pero cuando estás a las 2am debugeando producción, vas a agradecer tener límites claros entre responsabilidades.