En particulier pour des tests unitaires, j'essaie de mettre en œuvre une application avec A superposée Architecture. Je codis dans C # et en utilisant ASP.NET Web API pour la couche de service. Je vis à une architecture à 3 couches:
Je suis mal à l'aise de devoir choisir entre une architecture détendue des classes dal ou anémiques et je me demande si c'est parce que je m'approche de ce mauvais sens. Lorsque je me réfère à "Architecture détendue Dal", je veux dire un dal qui peut être appelé par couches autre que Le BLL directement au-dessus de celui-ci, c'est-à-dire la couche de service.
Ma raison de ne pas vouloir une architecture détendue DAL: facilite la contournement de la logique des entreprises.
Ma raison pour ne pas vouloir de classes BLL anémiques: ne voulez pas une pléthore de classes qui ne font rien d'autre que de passer des appels entre la couche de service et DAL.
Voici un exemple de code. Premièrement, une méthode de la couche de service soutenant l'enregistrement d'un étudiant dans une classe:
public class ClassRegistrationController : ApiController
{
private readonly BusinessLogic.ClassRegistration _classRegistrationLogic;
public ClassRegistrationController(
BusinessLogic.ClassRegistration classRegistrationLogic)
{
_classRegistrationLogic = classRegistrationLogic;
}
[Route("/api/class/{classId}/register")]
[HttpPost]
public IHttpActionResult RegisterForClass(
int classId,
[FromBody]ClassRegistrationRequest registrationRequest)
{
try
{
_classRegistrationLogic.RegisterStudentForClass(
classId,
registrationRequest.StudentId);
}
catch (ClassFullException)
{
return BadRequest("Cannot register for class, it is full");
}
return Ok();
}
}
Et la classe de logique d'entreprise qui le soutenait:
public class ClassRegistration
{
private DataLayer.IClassDataLayer _classDataLayer;
private DataLayer.IClassRegistrationDataLayer _classRegistrationDataLayer;
public ClassRegistration(
DataLayer.IClassDataLayer classDataLayer,
DataLayer.IClassRegistrationDataLayer classRegistrationDataLayer)
{
_classDataLayer = classDataLayer;
_classRegistrationDataLayer = classRegistrationDataLayer;
}
public void RegisterStudentForClass(int classId, int studentId)
{
var classDetails = _classDataLayer.GetClassDetails(classId);
if (classDetails.ClassIsFull)
{
throw new ClassFullException();
}
_classRegistrationDataLayer.AddStudentToClass(classId, studentId);
}
}
Les interfaces de la couche de données (la mise en œuvre ne correspond pas à la question):
public interface IClassDataLayer
{
Models.ClassDetails GetClassDetails(int classId);
}
public interface IClassRegistrationDataLayer
{
int AddStudentToClass(int classId, int studentId);
}
Maintenant, je pense Tout cela semble raisonnable et chaque classe a une seule responsabilité (et non négligeable). Mais, disons maintenant que je souhaite ajouter une méthode de couche de service qui expose les détails d'une classe, comme:
[Route("/api/class/{classId}")]
[HttpGet]
public IHttpActionResult GetClassDetails(int classId)
{
var classDetails = _classDetailsGetter.GetClassDetails(classId);
if (classDetails == null)
{
return NotFound();
}
return Ok(classDetails);
}
La question est: _classDetailsGetter
Soyez une instance d'une classe du DAL ou de la BLL?
Si cela vient de DAL, je dis efficacement que c'est "OK" pour que la couche de service soit directement à la DAL, qui ouvre le risque de, disons, une maintenance de la méthode de la couche de service RegisterForClass
décider d'aller directement à IClassDataLayer
au lieu de BusinessLogic.ClassRegistration
.
Mais si cela vient de la BLL, cette classe BLL ne serait responsable que de la cartographie des appels de 1 à 1, qui me frotte également le mauvais sens. Comme:
public class ClassDetailsLogic
{
private readonly DataLayer.IClassDataLayer _classDataLayer;
public ClassDetailsLogic(DataLayer.IClassDataLayer classDataLayer)
{
_classDataLayer = classDataLayer;
}
public Models.ClassDetails GetClassDetails(int classId)
{
return _classDataLayer.GetClassDetails(classId);
}
}
Y a-t-il des raisons de favoriser l'architecture détendue DAL ou l'ANMIC BLL? Est-ce que mes préoccupations concernant l'un (ou l'autre) non fondée? Ou y a-t-il autre chose que je manque?
Les objets de transfert de données (DTO) ont un seul but, et un seul but uniquement: récupérer des données d'une base de données et travailler avec elle sous une forme orientée objet.
Il n'y a pas d'autre chose qu'un "objet anémique BLL". C'est soit un objet BLL de première classe, soit un DTO. C'est rarement les deux.
Il y a des raisons pour lesquelles vous avez des choses comme des objets ViewModel. Un objet de modèle d'affichage contient des données de votre modèle, mais elle est adaptée à une vue. Ce n'est pas un DTO, du moins pas dans le sens où vous transférez des données d'une base de données. C'est un objet à part entière, ayant son objectif propre.
Alors, d'où quitte-t-il la couche logique de votre entreprise?
L'objectif d'une couche logique commerciale est de convertir les opérations de domaine d'activité (telles que transférer de l'argent de transfert ou du widget de construction) dans les opérations de CRUD. Cela n'effectue pas réellement les opérations de crud, cependant; C'est ce que faire votre DAL et DTP.
Cela ressemble à beaucoup d'objets, n'est-ce pas? Mais vos DTO vont généralement être générés par votre mappeur d'objet-relation, à moins que vous ne prévoyez de créer la base de données à partir de votre domaine d'objet (technique parfaitement valide).
Alors, comment épousez-vous votre code de logique d'entreprise à vos objets d'entreprise? Une façon de le faire est avec des classes partielles. C # vous permet de définir votre propre classe à côté du DTO généré par code, qui ajoutera votre validation personnalisée ou quelle que soit la logique commerciale que vous choisissez d'ajouter à l'objet Domaine d'activité. Étant donné que votre logique écrite à la main est dans une classe distincte (ayant le même nom), votre code sera protégé contre celui qui est écrasé par la classe générée par le code, si vous choisissez de ré-générer votre DTO. Vos cours DAL ne seront plus anémiques, car vous leur aurez ajouté votre propre intelligence via des classes partielles.