Several tables that should represent a single entity

Asked

Viewed 190 times

7

I am updating a system that has eleven tables that represent a single entity, I believe by implementation error, as an example below:

Entidade: Foo

Tabela Foo1 - Campo1, Campo2, Campo3
Tabela Foo2 - Campo1, Campo2, Campo3
Tabela Foo3 - Campo1, Campo2, Campo3, Campo4
[...]

Because of this implementation, there are eleven entities in the system that inherit from Foo, with their respective repositories and services. This implementation has had a direct impact on the code, which n switchs defining which entity should be used. The cases have the same code, with the exception of the repositories and services used.

Database and entity refactor is not currently an option.

Against this background, what would be the best approach to deal with this situation? The code made so far should not be changed, but there is a great effort to create new functionalities that depend on this architecture of N tables and N entities that should represent only one. Is there any Pattern design that can facilitate this work?

The project architecture is in Onion with MVC presentation layer. The MVC layer accesses the services that in turn encapsulate the system’s business rule. A common problem of this implementation is that whenever something involves these entities and tables, a switch needs to be made to define which service will be called to communicate with the database.

public class FooService{
    private service1;
    private service2;
    //demais services das entidades

    //construtor

    public void Inserir(Entidade foo)
    {
        switch(bar.TipoItem)
        {
            case(TipoItemEnum.Tabela1):
                service1.RealizarAlgumProcesso(foo);
                break;
            case(TipoItemEnum.Tabela2):
                service2.RealizarAlgumProcesso(foo);
                break;
            //demais cases para os demais services.
        }   
    }
}
  • Which version of . Net is the project?

  • At first glance, it seems to me a case for a Factory, could give us more information?

  • is using MVC 4. @Intruder, could specify what information you would like to know?

  • 1

    All Services also implement the same interface?

  • Each service implements a different interface, @Tobymosque

  • 1

    I advise you to make this fact very explicit in your question, and give different names to the method of each service, I believe that others have had this same intention, because the answers are following this line of reasoning.

  • services implement a common interface?

  • As reported to @Tobymosque, services implement different interfaces.

  • Are these services in a local library or are they web services (eg: WCF)? Is it possible for all services to implement a.common interface? Or are they really very different procedures for each entity?

  • @Tobymosque, the procedures are similar yes, with few changes in the business rule, however, due to the fact that there are 12 tables and 12 entities, the whole system was designed as if these functionalities were separate.

  • 1

    @Vinícius, all right, the interfaces are different, but the attributes and methods have different signatures? Or it could be encapsulated in a subsystem and let the polymorphism do the rest through a facade (facade)?

  • I agree with @Intruder that this may look like Stabbing, despite having a Bad Smell requesting refactoring... :) Following is a legal link to the pattern: http://www.dofactory.com/net/facade-designpattern

  • @Intruder, attributes vary little and methods could be common. Unfortunately, today is not like this, but for the features that will come can be done. The facade seems to be a good idea, although this approach runs away from Onion architecture. Creating a facade would not be similar to creating a common service for all these tables?

  • 1

    @Vinicius, to choose, think about the long term. I suggested this (facade) because you said it is not possible to refactor, but the work of concentrating the complexity of creating objects in your scenario I think should be done with a Factory. If the methods and attributes are clearly similar, it is very likely that refactoring is easy by including an interface and causing services to inherit from it, depending on the similarity, you would only clean up the scattered Witches and Factory does the rest. Doing the service will have the same effect as the Facade, but the facade is a design pattern.

Show 9 more comments

3 answers

3

Yes. Her name is Helper, which is a variation of Utility.

Since the methods are very similar, you can centralize the common business logic within the Helper and call only one method that does everything.

Please put examples of business logic in the question I put in the answer how to refactor your logic to a Helper.


Service Layer

Assuming your example, I can reimplementate a Service using a generic approach. For example:

public class ClasseService<T>
    where T: Foo 
{
    public void RealizarAlgumProcesso(T objeto) 
    {
        ...
    }
}

Specific verifications of the derivative type can be done as follows:

if (obj.GetType() == typeof(Foo1)) { ... }

I know it’s not quite right yet, but it’s a starting point.

  • Edited question, gypsy.

1

No . Net 4.0 we have the Dynamic option, I would not recommend the use lightly, but in your situation may come in handy.

Create a Factory that takes as parameter the table type and returns the corresponding service, to avoid refactoring the code and create an interface(s) and polymorphically program you can use Dynamic and invoke the method in Runtime. The counterpoint is Intellisense performance and loss.

This will only work if all services have the same method name.

Ex:

    public static class MyFactory
    {
        public static dynamic GetService(TableType type)
        {
            switch (type)
            {
                case TableType.Table1:
                    return new Service1();
                case TableType.Table2:
                    return new Service2();
                case TableType.Table3:
                    return new Service3();
                default:
                    return null;
            }
        }
    }

    public enum TableType 
    {
        Table1,
        Table2,
        Table3,
    }


public class Service1 { public int Xpto(int i) { return i; } }
public class Service2 { public double Xpto(double d) { return d; } }
public class Service3 { public string Xpto(string s) { return s; } }

Use with return and different parameters:

var service = MyFactory.GetService(TableType.Table1);
Console.WriteLine(service.Xpto(1));

service = MyFactory.GetService(TableType.Table2);
Console.WriteLine(service.Xpto(4.0));

service = MyFactory.GetService(TableType.Table3);
Console.WriteLine(service.Xpto("Xpto"));

0

The architecture of the services must follow the same architecture of the entities and for this you must use the polymorphism of their service objects and encapsulate only what each one needs. In the given example, a more flexible implementation would be to create a generic interface that contains the method to be used and create a service for each type of entity that implements that interface. This will require a selector class, which is the one that will only have the logic of what is on the switch. This way all code will be with due encapsulation. Follows the refactoring of the example you posted:

public class FooService
{
    public void Inserir(object foo)
    {
        IService insertService = InsertServiceSelector.GetService(foo);
        insertService.RealizarAlgumProcesso(foo);
    }
}

interface IService
{
    void RealizarAlgumProcesso(object foo);
}

class StringService : IService
{
    public void RealizarAlgumProcesso(object foo)
    {
        string bar = (string) foo;
        /* ...Serviço para Strings... */
    }
}

class IntegerService : IService
{
    public void RealizarAlgumProcesso(object foo)
    {
        int bar = (int) foo;
        /* ...Serviço para inteiros.. */
    }
}

class InsertServiceSelector
{
    public static IService GetService(object foo)
    {
        if(foo is string)
            return new StringService();

        if (foo is int)
            return new IntegerService();

        /*... A lógica de seleção fica encapsulada...*/

    }
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.