2010-09-15 26 views
23

Si tengo esos dos proyectos:¿Cómo tratar con referencias circulares?

MyCompany.ERP.Billing 
MyCompany.ERP.Financial 

facturación pregunta/envía información a financiera y viceversa. Ambos son demasiado grandes, así que no quiero ponerlos en un solo proyecto. Visual Studio no permite referencias circulares. ¿Cómo lidiarías con eso?

+3

Si bien puede emplear varios trucos ya mencionados, esto suena como un problema arquitectónico. ¿Cómo se hace una distinción entre Facturación y Financiera? Me parece algo difícil trazar una línea aquí, y mi sensación es "financiera" nunca debería referirse a la facturación, porque es mucho menos abstracta. ¿Es posible que algunos elementos residan en el proyecto equivocado? ¿Qué es exactamente "demasiado grande"? – mnemosyn

+1

Eso es solo un ejemplo. La intención es saber cómo crear un ERP siguiendo buenas prácticas de OOP ya que las referencias circulares son muy comunes en ese tipo de software porque es muy grande y se divide en muchos módulos. – Eduardo

+3

Las referencias circulares provienen de un diseño defectuoso, no de la complejidad del dominio. Sin juego de palabras, no estoy diciendo que esta es la situación aquí. Los ERP no son algo que pueda desarrollar a medida que avanza por el camino, necesita tener una imagen muy clara de lo que está tratando de lograr mucho antes de escribir una sola línea de código. – devnull

Respuesta

23

Extraiga las interfaces de sus clases y colóquelas en un proyecto central al que se hace referencia desde los proyectos Billing y Financial. A continuación, puede usar esas interfaces para compartir datos entre ensamblajes.

Esto solo le permite pasar objetos entre esos 2 conjuntos, pero no puede crear objetos desde el otro puesto que en realidad no tiene una referencia para comenzar. Si desea poder crear objetos, necesita una fábrica, externa a esos 2 proyectos, que maneje la creación de objetos.

Extraería la lógica de negocio que necesita para compartir los datos entre Billing y Financial en otro proyecto. Esto facilitaría las cosas y le evitaría recurrir a todo tipo de trucos que hacen que el mantenimiento sea una pesadilla.

+0

¿Y cómo creo instancias de objetos financieros dentro de la facturación? Ejemplo: IAccountPayable a = new ???() – Eduardo

+0

Los objetos de transferencia de datos que se deben usar como parámetros y valores de retorno para pasar entre Billing y Financial deben definirse en el ensamblado compartido, junto con las interfaces de servicio. Las implementaciones del servicio idealmente se construirían a través de un marco de inyección de dependencia y se proporcionarían a través de argumentos de constructor a las clases que dependen de ellos. – StriplingWarrior

+0

Acepto que esta es una posibilidad, pero requiere el uso de ServiceLayer (inteligente) y DTOs. Sin embargo, si el OP está implementando DomainModel, eso no funcionará. Pero si DomainModel es el camino a seguir o no depende de muchos criterios, no siempre se puede elegir que sean DTO, en los que todo el enfoque dará lugar a un código engorroso. Me recuerda a los muchos peligros que vienen con los "Patrones de Integración Empresarial" ... – mnemosyn

5

Tener un proyecto demasiado grande no debería ser un problema. Puede mantener su código estructurado con espacios de nombres y diferentes carpetas para el código fuente. En este caso, las referencias circulares ya no son un problema.

+1

-1. Si bien la declaración es * precisa *, se pierde el sentido de la pregunta. –

+1

Estaba respondiendo al enunciado de la pregunta "Ambos son demasiado grandes, así que no quiero ponerlos en un solo proyecto". Creo que el uso de carpetas y espacios de nombres es la solución correcta para resolver este problema. Se deben realizar ensamblados adicionales cuando sea necesario implementar las partes de la aplicación de forma independiente. – PhilB

+3

Uno de los problemas de los proyectos grandes es que muchas personas realizan cambios en el archivo .csproj, lo que provoca demasiadas fusiones. – Eduardo

3

La respuesta que menciona interfaces es correcta, pero si necesita poder crear ambos tipos de ambos proyectos, tendrá que crear una fábrica en otro proyecto (que también haría referencia al proyecto de interfaces pero podría ser referenciado por sus dos proyectos principales) o cambiar significativamente la estructura que está usando.

Algo como esto debería funcionar:

Finance: References Billing, Interfaces, Factory 
Billing: References Finance, Interfaces, Factory 
Factory: References Interfaces 

fábrica tendría un BillingFactory.CreateInstance() As Interfaces.IBilling y también en la clase abstracta de facturación que implementan Interfaces.IBilling.

El único problema que puedo ver es si necesita hacer algo inteligente al crear un objeto y no desea que esa lógica termine en un proyecto separado, pero como no ha mencionado ninguna lógica inteligente para crear instancias, esto debería ser suficiente

+1

¿Quieres decir algo como esto? MyCompany.ERP.Factories.Billing.CreateInstance (typeof (IAccountPayable)) – Eduardo

+0

Sí - Es entonces la responsabilidad de la fábrica crear su instancia concreta y devolverla. Esto se puede llamar desde cualquier otra ubicación. Donde la clase abstracta AccountPayable que implementa IAccountPayable lives dependerá de cuán compleja sea: si es simplemente un POCO, puede vivir en un lugar simple (es decir, en lugar de un proyecto de interfaces, tener un proyecto común o de entidades). Si es complejo y tiene su propia lógica, necesitará reestructurarlo levemente, ya que no desearía que toda su lógica empresarial se diseminara en proyectos comunes ... – Basic

+0

Usamos: un ensamblado Project.BusinessLogic que contiene "Administradores" ". Los gerentes son clases que tratan con entidades en un ensamblado de Project.Entities: la UI habla con los gerentes que mantienen la lógica para manipular las entidades. Los gerentes pueden manipular o devolver entidades. Las entidades en nuestro caso son POCO muy simples. Los administradores también tienen un método CreateInstance() que devuelve una nueva entidad. – Basic

0

This solution podría terminar como una solución para el problema de referencia circular. Básicamente, utiliza la lógica #if alrededor del código que no se compila a menos que exista la referencia, y utiliza la compilación condicional en el archivo del proyecto para definir una variable solo si existe el ensamblado necesario. Como resultado, en la primera descarga desde la fuente, o después de una solución limpia, debe compilar dos veces. Las compilaciones/reconstrucciones posteriores solo requieren 1 compilación de forma normal. Lo bueno de esto es que nunca tienes que comentar/descomentar manualmente las declaraciones #define.

Cuestiones relacionadas