Cuando trabaje en aplicaciones, a menudo necesitará representar un grupo de constantes en la lógica empresarial e incluso en las capas de dominio. Sin embargo, debe evitar el uso de tipos de enumeración o enumeraciones en la capa de dominio y, en su lugar, utilizar alternativas como tipos de registros.
¿Por qué? En este artículo, explicaremos las desventajas de usar enumeraciones en la capa de dominio.
Crear un proyecto de aplicación de consola en Visual Studio
En primer lugar, creemos un proyecto de aplicación de consola .NET Core en Visual Studio. Suponiendo que Visual Studio 2022 esté instalado en su sistema, siga los pasos que se describen a continuación para crear un nuevo proyecto de aplicación de consola .NET Core.
- Inicie el IDE de Visual Studio.
- Haga clic en «Crear nuevo proyecto».
- En la ventana «Crear nuevo proyecto», seleccione «Aplicación de consola (.NET Core)» de la lista de plantillas que se muestran.
- Haga clic en Siguiente.
- En la ventana «Configura tu nuevo proyecto», especifica el nombre y la ubicación del nuevo proyecto.
- Haga clic en Siguiente.
- En la ventana «Información adicional» que se muestra a continuación, elija «.NET 8.0 (soporte a largo plazo)» como la versión del marco que le gustaría utilizar.
- Haga clic en Crear.
Usaremos este proyecto de aplicación de consola .NET 8 para trabajar con los ejemplos de código que se muestran en las secciones siguientes de este artículo.
¿Qué hay de malo con las enumeraciones?
Si bien los tipos de enumeración pueden proporcionar flexibilidad para mantener un conjunto de valores constantes en su aplicación, a menudo introducen un estrecho acoplamiento entre el modelo de dominio y el código que lo utiliza, lo que limita su capacidad para evolucionar el modelo de dominio.
Considere la siguiente enumeración utilizada para definir roles de usuario y administrador.
public enum Roles { User, Administrator, Reviewer, SuperAdmin }
También puede utilizar una enumeración para definir un grupo de constantes como se muestra en el siguiente fragmento de código.
public enum Roles { User = 1, Administrator = 2, Reviewer = 3, SuperAdmin = 4 }
Problema 1: la encapsulación huele
Ahora, supongamos que necesita saber si un rol en particular se relaciona con un rol de administrador. El siguiente fragmento de código ilustra un método de extensión llamado IsAdmin que verifica si una función en particular es Administrador o SuperAdmin.
public static class RolesExtensions { public static bool IsAdmin(this Roles roles) => roles == Roles.Administrator || roles == Roles.SuperAdmin; }
Esto nos lleva al primer problema con el uso de enumeraciones en la capa de dominio. Aunque puede operar en la enumeración usando métodos de extensión, su código rompe el principio de encapsulación porque la lógica para consultar el modelo y el modelo que creó están separados. En otras palabras, la lógica que verifica el modelo no está dentro de la misma clase. Este es un antipatrón, y un modelo de este tipo a menudo se conoce como modelo anémico.
Problema 2: código espagueti
Otro problema con las enumeraciones: es posible que a menudo necesites usar conversiones explícitas en el código de tu aplicación para recuperar un valor de una enumeración. La siguiente línea de código ilustra esto.
int role = (int)Roles.User;
Los elencos explícitos no son un buen enfoque. Siempre son costosos en términos de rendimiento e implican que ha utilizado tipos incompatibles en su aplicación o que los tipos no se han definido correctamente. El uso de enumeraciones en la capa de su dominio podría llevar al uso de conversiones explícitas en toda su aplicación, lo que satura el código y dificulta su lectura y mantenimiento.
Problema 3: restricciones de nombres
Recuerde, no puede incluir caracteres de espacio en los nombres de las constantes de enumeración en C#. Por lo tanto, el siguiente código no es válido en C#.
public enum Roles { Admin, Super Admin }
Puede aprovechar los atributos para superar esta limitación.
using System.ComponentModel.DataAnnotations; public enum Roles { Admin, [Display(Name = "Super Admin")] SuperAdmin }
Sin embargo, tendrá problemas cuando su aplicación necesite brindar soporte para diferentes configuraciones regionales.
Utilice tipos de registros en lugar de enumeraciones
Una mejor alternativa es utilizar tipos de registros. Puede aprovechar los tipos de registros para crear un tipo inmutable como se muestra en el fragmento de código que se muestra a continuación.
public record Roles(int Id) { public static Roles User { get; } = new(1); public static Roles Administrator { get; } = new(2); public static Roles Reviewer { get; } = new(3); public static Roles SuperAdmin { get; } = new(4); }
Un objeto inmutable es un objeto que, una vez creado una instancia, no se puede modificar. Por lo tanto, los registros poseen seguridad intrínseca para los subprocesos e inmunidad a las condiciones de carrera. Los objetos inmutables también hacen que su código sea más legible y fácil de mantener.
Un beneficio importante de utilizar tipos de registros es preservar la encapsulación porque cualquier método de extensión que escriba puede ser parte del modelo en sí. Recuerde, no puede incluir ningún método dentro de una enumeración.
Además, los registros facilitan el suministro de nombres significativos, como lo ilustra el siguiente código.
public record Roles(int Id, string Name) { public static Roles User { get; } = new(1, "User"); public static Roles Administrator { get; } = new(2, "Administrator"); public static Roles Reviewer { get; } = new(3, "Reviewer"); public static Roles SuperAdmin { get; } = new(4, "Super Admin"); public override string ToString() => Name; }
Ahora puede acceder a las constantes del registro Roles de la misma manera que puede acceder a las constantes de enumeración.
Roles admin = Roles.Administrator; Roles user = Roles.User; Roles reviewer = Roles.Reviewer; Roles superAdmin = Roles.SuperAdmin;
Cuando invoca el método ToString(), el nombre de la constante se mostrará en la ventana de la consola como se muestra en la Figura 1.
Alternativamente, puedes usar una clase en lugar de un tipo de registro y luego definir las constantes que necesitas. Sin embargo, siempre preferiría un tipo de registro por motivos de rendimiento. Los tipos de registros son tipos livianos por lo que son mucho más rápidos que las clases. Un registro es en sí mismo un tipo de referencia, pero utiliza su propia verificación de igualdad incorporada, que verifica por valor y no por referencia.
Copyright © 2024 IDG Communications, Inc.