Le tecniche di refactoring dell'architettura pulita di Uncle Bob per gli oggetti dati / entità / query LINQ

0

Ho una logica di controller relativamente semplice con parti brutte della creazione di oggetti. Alcuni degli oggetti sono davvero enormi e anche se tutto il controller è quello di restituire l'oggetto, sembra disordinato e difficile da leggere. Quindi vorrei chiedere come posso rendere questi casi più puliti. Lascia che ti mostri alcuni esempi:

Ad esempio, fai attenzione alla creazione di viewmodel:

public async Task<ActionResult> Edit(Guid id)
{
    if (id.IsValidGuid())
    {
        // Get User Details
        var userTask = ApiGatewayService.GetUserDetail(id, AuthService.BearerToken);
        var nationalitiesTask = ApiGatewayService.GetNationalityList(
            new FilterParameters(), 
            AuthService.BearerToken
        );
        var countriesTask = ApiGatewayService.GetCountryList(
            new FilterParameters(), 
            AuthService.BearerToken
        );
        await Task.WhenAll(userTask, nationalitiesTask, countriesTask);
        var user = userTask.Result;
        var nationalities = nationalitiesTask.Result;
        var countries = countriesTask.Result;
        if (user is null)
            return NotFound();
        var userAddress = user.PrimaryAddress;
        var userHomePhone = user.Phones.FirstOrDefault(
            x => x.Type == PhoneType.LandLine
        );
        var userMobilePhone = user.Phones.FirstOrDefault(
            x => x.Type == PhoneType.Mobile
        );

        // View Model
        var viewModel = new EditUserViewModel
        {
            User = user,
            Id = user.Id,
            FirstName = user.FirstName,
            LastName = user.LastName,
            BirthDay = user.Detail?.BirthDay,
            Email = user.Email,
            Nationality = user.Detail?.Nationality?.Id,
            Title = user.Detail != null ? user.Detail.Title : TitleType.Mr,
            ProfilePhotoName = user.Detail?.ProfilePhotoName,
            MarketingOptin = user.Detail != null 
                ? user.Detail.MarketingOptin 
                : false,
            ChangePassword = false,
            Password = null,
            Address_City = userAddress?.City,
            Address_Country = userAddress?.Country.Id,
            Address_HouseName = userAddress?.HouseName,
            Address_HouseNumber = userAddress?.HouseNumber,
            Address_Line = userAddress?.AddressLine,
            Address_PostCode = userAddress?.PostCode,
            Address_StreetName = userAddress?.StreetName,
            Home_CountryCode = userHomePhone?.CountryCode?.ToString(),
            Home_PhoneNumber = userHomePhone?.PhoneNumber,
            Mobile_CountryCode = userMobilePhone?.CountryCode?.ToString(),
            Mobile_PhoneNumber = userMobilePhone?.PhoneNumber,
            CountriesList = countries.List,
            NationalitiesList = nationalities.List
        };
        return View(viewModel);
    }
    else
        return RedirectToAction("Index");
}

O un altro usando LINQ con un'enorme selezione

public CompanyDetailModel GetCompanyDetails(Guid accountHolderId)
{
    CompanyDetailModel companyDetailModel = new CompanyDetailModel();

    var accountHolder = _dbContext.AccountHolders.FirstOrDefault(
        x => x.Id == accountHolderId
    );

    var companyId = accountHolder.ObjectId;
    var company = _dbContext.Companies.FirstOrDefault(x => x.Id == companyId);

    companyDetailModel.CompanyId = company.Id;
    companyDetailModel.Description = company.Description;
    companyDetailModel.Name = company.Name;
    companyDetailModel.RegistrationNumber = company.RegistrationNumber;
    companyDetailModel.VATNumber = company.VATNumber;

    var users = 
        from ah in _dbContext.AccountHolders
        join uc in _dbContext.UserCompanies 
            on ah.ObjectId 
            equals uc.CompanyId
        join u in _dbContext.Users 
            on uc.UserId 
            equals u.Id
        where ah.Id == accountHolderId
        select new CompanyUserModel
        {
            UserId = u.Id,
            CompanyId = (Guid)uc.CompanyId,
            FullName = u.FirstName + " " + u.LastName,
            Email = u.Email,
            PhoneNumber = u.PhoneNumber,
            UserCompanyRoleType = uc.Role
        }
    ;

    var invites = 
        from ci in _dbContext.UserCompanyInvites
        select new CompanyUserInviteModel
        {
            Id = ci.Id,
            CompanyId = ci.CompanyId,
            IsAccepted = ci.IsAccepted,
            UserEmail = ci.UserEmail,
            RoleType = ci.RoleType
        }
    ;

    companyDetailModel.CompanyUsers = users.ToList();
    companyDetailModel.CompanyUserInvites = invites.ToList();

    return companyDetailModel;
}

È sufficiente prendere la creazione di oggetti su un metodo privato o esiste un modo migliore? Non sarebbe davvero noioso impostare un mapper?

    
posta Yamaç Kurtuluş 28.11.2018 - 01:11
fonte

2 risposte

0

Sia che si sposti questa logica in modelli di visualizzazione o mappatori di dati, si sta verificando la logica di mappatura dei dati. L'impostazione di un mapper dei dati non è più noiosa del codice già presente nei controller. Questa sembra essere più una domanda sull'estetica del codice. "Sembra" disordinato nel controller e tu vuoi controller "puliti". Questo può essere ottenuto in uno dei seguenti modi:

  1. Spingi questa logica nei modelli di vista, in cui i modelli di vista contengono riferimenti alle tue entità. Questo è stato già menzionato da Ewan.

    La cosa bella di questo approccio è che la logica per applicare le modifiche alle tue entità è contenuta nei tuoi modelli di visualizzazione. Lo svantaggio è che la logica per applicare le modifiche alle entità è contenuta nei modelli di visualizzazione. A volte è bello mantenere i dati nel modello di visualizzazione completamente separati dalle entità in modo che siano liberi di evolvere in modo diverso e per diversi motivi. Questo porta a:

  2. Utilizza i costruttori nei tuoi modelli di vista per eseguire il mapping, ma i modelli di visualizzazione non contengono riferimenti alle entità e invece mappano direttamente ad altre proprietà.

    Esiste ancora un accoppiamento tra il modello di vista e l'entità, ma solo nei costruttori. Hai un'altra entità che deve essere mappata allo stesso modello di vista? Aggiungi un costruttore. Questo funziona bene per i modelli di visualizzazione piccoli e semplici.

    Il che porta allo svantaggio di questo approccio: il costruttore gonfia. Visualizza i modelli diventano 90% codice costruttore e il 10% "materiale utilizzato dalla vista."

  3. Utilizza un "view model factory" per creare i modelli di visualizzazione basati sulle entità.

    Visualizza modelli ed entità completamente disaccoppiati e liberi di evolversi separatamente. Quando una delle due parti cambia, viene interessato solo il factory del modello di visualizzazione.

    Funziona bene per modelli di viste complessi, o casi in cui i modelli di vista e / o le entità cambiano spesso, e per ragioni diverse. La fabbrica del modello di vista è dove si incontrano i modelli di vista, le entità e i repository.

    È un altro livello che isola gli altri livelli contro il cambiamento.

    Quest'ultimo bit può essere il punto di non ritorno tra le soluzioni. Quando la costruzione di un modello di visualizzazione diventa complessa, l'utilizzo di una fabbrica di modelli di visualizzazione che implementa un'interfaccia e quindi l'iniezione nel controller consente di simulare facilmente questa complicata logica quando l'unità verifica i controller.

La soluzione scelta risulterà in un codice del controller "più pulito", che sembra essere il tuo obiettivo finale qui. Ho usato una combinazione di numeri 2 e 3 prima, con il Numero 2 usato per i modelli a vista semplice e il Numero 3 usato per i modelli di viste complesse, o per visualizzare i modelli che rappresentano un'intera "pagina" o "schermata" nell'applicazione .

Con gli approcci 2 e 3, avrai bisogno di un ulteriore livello per applicare le modifiche ai dati nei modelli di visualizzazione alle tue entità. Puoi usare la nebulosa "classe di servizio" o andare con un caso d'uso in architettura pulita per questo.

In entrambi i casi, hai bisogno del codice per mappare i dati dalle entità per visualizzare i modelli, e poi tornare indietro. Questo non cambierà. Tutto quello che puoi fare è renderlo più bello e, si spera, applicare nomi significativi alle cose.

    
risposta data 28.11.2018 - 14:54
fonte
0

Onestamente non penso sia troppo male. La cosa fondamentale è che il controller ha una sola responsabilità.

Se vuoi renderlo un po 'più pulito puoi spostare la mappatura del ViewModel stesso per esempio.

public class EditUserViewModel
{
    private User user;
    EditUserViewModel(User user) {this.user = user;}

    public bool MarketingOptin 
    {
        get 
        {
            return user.Detail != null ? user.Detail.MarketingOptin : false,
        }
        set {...}
    }
}
    
risposta data 28.11.2018 - 11:48
fonte

Leggi altre domande sui tag