Communication between layers Factory Approach - servinglynk/hslynk-open-source-docs GitHub Wiki
We are streamlining / channelling access to Business layer components (part of Core Service Layer) and DAO layer components (part of Core Model layer), by adding a factory bean.
Sandeep's Reply: This streamlining is unnecessary.Channelizing is of use when we are in a fear of having a lot of spring beans autowired to a component.The very reason why Spring framework is popular is because of its easy dependency injection and adding a Service Locator/Factory layer is an over kill of Spring framework.
Just assume attributes of an employee - name, age, joining date , designation. If we need to access these attributes, we normally define an "Employee" class and declare these attributes with in that class, provide setters and getters, but do not access them directly.
The same way, assume that all business layer components are attributes of service layer , We are just defining a class "Service Factory" for these components (like EMPLOYEE class in the example above) and provide getters and setters.
We add a Spring bean and call that as a Factory. Essentially setting a registry of all dependencies and asking every one to go get it from registry.
In our spring configuration file, once we configure the business layer components
<bean name="enrollmentService" class="com.hmis.business.service.EnrollmentService"/> <bean name="clientTypeService" class="com.hmis.business.service.ClientTypeService"/>
We just need to register the component with ServiceFactory, as below.
<bean name="serviceFactory" class="com.hmis.business.service.ServiceFactory"> <property name="enrollmentService" ref="enrollmentService" /> <property name="clientTypeService" ref="clientTypeService" /> </bean>
So, adding a spring bean that acts as a factory bean, nothing else.
Sandeep's Reply: With the spring DI approach you don't have any overheads of adding any bean related entries into an XML file. It is one less step to remember. Just @Autowiring the required bean gets you its reference.
Channelled interface for communication
Please check both the diagrams. By defining a channelled interface between layers we have maintainable and controlled code . We don't have inject different business layer components in different end points , and we don't have inject different DAO layer components in different business layer components.
We can just inject this "service factory" in a super class for all the REST end points and we are done. Likewise, We can inject this "persistence factory" in a super class for all Business layer components.Consistent / Cleaner code base
Code is not cluttered, with several bean injections across several components of the system. All components just talk to one single bean. This ensures a consistent programming / coding standard to anyone who is contributing the code base.
Sandeep's Comment: Let us ask ourselves a question how many spring beans will we be autowiring to a component ? As of now I don't see more than 5-6 spring beans being autowired. So why do we bother adding an additional layer.Centralized Control
As access is defined through the "factory' getters , we can have a controlling logic in the factory . For instance, factory can hand out different instances of same interface based on the criteria of the caller.
Sandeep's Comment: Not sure about the above point to get different instances(did you mean implementation) of the same interface. This can be easily achieved using Spring DI using @Qualifier annotation.Single point of entry for Auditing / Logging
As access is defined through the "factory" getters , we have single point of entry for auditing and logging.
Sandeep's Comment: Not sure why do we need a factory for that. Logging can happen in individual beans. Auditing is a different topic and I'm not sure how much auditing or logging can be performed using this approach. Normally I would either write a Spring aspect for auditing and use it at the Controller level or where ever I want.Inter and Intra layer communication through factory
Not only the inter layer communication, intra layer communication also will be streamlined. For instance, Enrolment business component , when need to use Notification business Component , it just asks service factory. Please refer to "blue" lines in Diagram 1 and "blue" lines in Diagram 2
Sandeep's Comment: It's an overkill of a Spring DI if we have to call a spring bean from another bean using a Factory verses using Spring DI directly.Think testing
When we need to test our code by injecting MOCK objects, we can simply change/replace service factory to include MOCK Business components (as shown below, just by defining mock objects), without touching any other configuration. <bean name="serviceFactory" class="com.hmis.business.service.ServiceFactory"> <property name="enrollmentService" ref="mockEnrollmentService" /> <property name="clientTypeService" ref="mockClientTypeService" /> </bean>However, without this approach, we need to change the actual business component references to point MOCK objects, as they are directly referred in our REST Layer.
Sandeep's Comment: Unit testing is simple and easy using Spring DI approach we don't have to have additional entries inside an xml file to run spring. All we need to do is add a @Mock(Mockito) for the bean being autowired.
Unnecessary layer of Abstraction.
Surya Yadavalli - A good abstraction is always important, as it reduces cohesion. Without having multiple components dealing with multiple dependencies, we are providing this abstraction so, all the components that we are going to build, do not need to explicitly know /inject any dependencies. Coming to our specific application, please consider this scenario, where this layer of abstraction plays major role. Our understanding is that, these micro services can communicate with each other either by Autowiring dependencies or by making a REST API call (if the module is totally independent, like notification service). In such scenario, we don't want either the @Controller components (REST APIs) or @Service Components (Business Layer components) to worry about what reference to wire. The factory abstracts that detail. so, regardless of how the communication is being made the call will still look like <pre> serviceFactory.getNotificationService().sendNotification(<parameters>); If we don't have abstraction we have to inject the dependencies as follows. @Autowired NotifiationServiceClient notificationServiceClient; (call through core client module, REST API) { notificationServiceClient.sendNotification(<parameters>); } In the future, if we don't want to go through Core Client module, but call directly, we need to change the dependencies in the code, as follows @Autowired NotificationBusienssComponent notificationBusinessComponent (direct call)
Sandeep's Comment: I prefer autowiring the exact bean this way @Autowired NotifiationServiceClient notificationServiceClient;
You could get the spring beans directly by auto wiring them and getting beans from a factory is an unnecessary step.
Surya Yadavalli - We are actually reducing the work, as we don't have "auto wire" any of the components, if we just have the 'servicefactory' reference available. Instead of accessing the property directly enrollmentService.createEnrollment() we make the call as shown below. factory.getEnrollmentService().createEnrollment(). So, no additional steps are necessary.
There is no huge benefit for the cost we pay by getting the beans from a Factory.
Surya Yadavalli - This statement is not a con to the approach suggested, but please refer to the benefits sectionFactory approach will make it hard to find service layer references.
Surya Yadavalli - Generic and not quite sure what this means. Please qualify with an example or detail.
Sandeep's Comments: When you want to find reference of a service layer bean you would always find the Factory class first and you need to drill down further to get to your controller. Verses with Spring DI when you find references it will point you the exact reference with out any factory layers.Spring may act funny when we get beans with different scope from a factory/service locator especially for prototype beans
Surya Yadavalli - Generic and not quite sure what this means. Please qualify with an example or detail. Secondly, This is not applicable to our scenario, as no business component or DAO component will be configured as a PROTOTYPE bean in our application. Please give a scenario, where we need to define a Business component or DAO component as PROTOTYPE.Every time we add a bean its an extra step to add its entry into the Factory.It is a simple step but still needs to be done
Surya Yadavalli - This actually is an advantage and reduces work. Instead of injecting / Autowiring these dependencies in our classes where ever we use them, we just set them up in the factory and go to factory to get the reference.If we ever want to get a different implementation of a bean we could always use springs @Qualifier("") to get us the appropriate bean
Surya Yadavalli - This statement is not a con to the approach suggested. Secondly, The statement is not true. Purpose of Qualifier different as it we can't have it determine what references to return at "runtime". We can PRE-DEFINE @Qualifier annotation to tell SPRING container what "implementation" of a bean we want it to return. However, having a factory gives us control at RUNTIME. Here is an example : When we need to send a notification, you can pass on USER preference to the FACTORY to hand out SMSNotification Component or EMAILNotification Component (depending on user Preference), which can't be accomplished using Qualifier.
Sandeep's Comment: Humbly, I don't know what is wrong in my statement about the @Qualifier annotations. Why would you want a different bean at runtime. And to accomplish this you would have to make Factory class input driven which adds further more complexity. Verses you could pick and choose the exact implementation well in advance and use Spring DI.
Sandeep's Comment: Conclusion: " My 2 cents" A Factory layer to get spring bean is an overkill of an Spring DI framework and would add a lot unnecessary work for the functionality it offers.
We need to write down pros and cons. And We can implement this only we if get tangible gain from this as of now. Sandeep • Mar 6, 10:25 AM Sandeep Dolia I also want to evaluate if this feature is absolutely required now or could be bring it later. Sandeep • Mar 6, 10:26 AM Surya Yadavalli ... Sandeep, This is a matter technical design, not a business feature to decide later in the game. You can accomplish anything with several technical ways. What you choose decides how efficiently you build the system. Surya • Mar 6, 10:29 AM Sandeep Dolia Surya, We could always refactor our code if we find a better design Sandeep • Mar 6, 10:30 AM Sandeep, maybe later I can subscribe to receive build failures, once it's more production ready. Mar 6, 10:30 AM Eric Jahn Surya Yadavalli Why, refactor ? when you already know what to do ? Surya • Mar 6, 10:30 AM Sandeep Dolia And that's why we need to discuss this out. Factory to me is an additional layer to get the Spring bean. I would rather @Autowire the spring bean directly And its real easy to find references. When we encounter a situation where we end up with 20 beans in a file then we can think of refactoring. I just want to be Agile here.... Lets talk over the phone and finalize this. Sandeep • Mar 6, 10:32 AM Surya Yadavalli ... Again I'm only talking about the service layer. I'm fine with a super class idea. But the idea to use factory to get a spring bean is the topic of discussion. Sandeep • Mar 6, 10:39 AM ... I'm fine with the Base Class approach the only thing I prefer is to autowire the spring beans directly verses getting it from a Factory. Sandeep • Mar 6, 10:49 AM Is it true that we are making some of the Spring DI functionality inaccessible by using a factory, or would that be the same? Mar 6, 10:50 AM Eric Jahn Surya Yadavalli Every thing is Spring Surya • Mar 6, 10:50 AM Sandeep Dolia Essentially the factory would contain getter and setters so that you could get the Spring bean from a factory. Please correct me if I'm wrong Surya. Sandeep • Mar 6, 10:51 AM Surya Yadavalli How we hand over beans is what we are talking about here, really independent of Technology or framework, but just talking about design Mar 6, 10:52 AM Eric Jahn https://github.com/servinglynk/OpenHMISDataWarehouse/wiki/Common-components-across-all-layers-(-Draft-and-Incomplete) ... Sandeep • Mar 6, 11:27 AM I've been going over your comments, and I think I have a clearer picture of the situation. Part of the difficulty I had was figuring out what was driving your interest in each approach. Sandeep's driving interest was easier to figure out, since he just wants to do things in the generic Spring way, which is typically DI. But, when I looked at your write-up, Surya, I think I now understand better what you're going after. Can I paraphrase what I think is your most compelling argument? It's this: "We can just inject this "service factory" in a super class for all the REST end points and we are done. Likewise, We can inject this "persistence factory" in a super class for all Business layer components." Mar 9, 10:38 AM Eric Jahn Surya Yadavalli I tried to show this from a design stand point in these diagrams https://github.com/servinglynk/OpenHMISDataWarehouse/wiki/Communication-between-Layers-%28Factory-Approach%29---Design Surya • Mar 9, 10:41 AM If we weren't doing this tandem approach where services can communicate either RESTfully *or* through some sort of autowiring/direct method calling, the DI approach would definitely be simpler and more common, in the Spring programming paradigm. But we are doing the tandem approach... and the Factory lets us call it the same way in our code regardless. I see that as the *only* really good reason to use the Factory approach, since it's pretty clear from the browsing I've done, that the DI would be preferable in a more monolithic architectural approach. But we're doing microservices, where services could be colocated or not, and it need to be dynamically handled. ... Mar 9, 10:46 AM Eric Jahn Surya Yadavalli ... One thing I want to also point out is, by doing this we are not going away with DI - instead of injecting multiple beans, we inject only one, the Factory. And all components are in turn injected in Factory (again DI). Surya • Mar 9, 10:47 AM Sandeep, with the DI approach alone (no Factory), how can we insert a same consistent piece of code to call another microservice, regardless of whether it is RESTfully called or just a direct method call? Mar 9, 10:49 AM Eric Jahn Sandeep Dolia All our @Service layer beans will be in 1 project And when it comes to intra-service communication it is easy to autowire them directly verses calling a factory to get the bean. Sandeep • Mar 9, 10:51 AM But won't that require different code depending on whether it's using a RESTful communication or not? Mar 9, 10:51 AM Eric Jahn Sandeep Dolia So if its a REST call, I would rather prefer making a REST call to an endpoint. So we can perform auditing from there. It will be 1 generic approach. Sandeep • Mar 9, 10:53 AM But the number and types of services on each server node will be configured dynamically, so we can't no in advance, when we are coding, which method will be used. err know in advance So handling it with one single Factory would solve that problem? Mar 9, 10:54 AM Eric Jahn Sandeep Dolia A Factory just returns you a spring bean. which you could either way autowire. Please correct me if I'm wrong Surya. Sandeep • Mar 9, 10:55 AM and the autowiring would be able to ascertain to use REST or not? I think the Factory might give us a place to place some differential handling logic... Mar 9, 10:56 AM Eric Jahn Sandeep Dolia I see that point. We can give a shot and see Sandeep • Mar 9, 10:57 AM So, if we can figure out a way around that without using a Factory, I'm all for it. Mar 9, 10:58 AM Eric Jahn Sandeep Dolia And if we later find out that the factory does not offer anything special we can always refactor our code. Sandeep • Mar 9, 10:58 AM Sandeep, let me give you a little time to consider this, before we make a decision. I'm all for simplicity, and if we can avoid the Factory, I'd rather avoid it. But I don't see a way to handle this dynamic node distribution of microservices... without it... Mar 9, 11:00 AM Eric Jahn Sandeep Dolia Let me ask you a question. Are you planning to use REST for intra-service communication ? Sandeep • Mar 9, 11:00 AM no, right? Mar 9, 11:01 AM Eric Jahn Sandeep Dolia I'm only talking about calls between @Service layer You are correct The answer is No. And when you know all the @Service spring beans will be in 1 jar file (HMISService.jar) Intra or inter service communication can be easily performed using Spring DI. Sandeep • Mar 9, 11:02 AM Might that be a difficult constraint that all the @Service spring beans will be in 1 jar file? Couldn't that get pretty large? Mar 9, 11:03 AM Eric Jahn Sandeep Dolia No actually they are in 1 jar as of now Sandeep • Mar 9, 11:03 AM But we have a lot more logic to add. Mar 9, 11:03 AM Eric Jahn Sandeep Dolia AuditService, NotificationService could be part another bean. Sandeep • Mar 9, 11:04 AM btw, James is taking a day off today to write up more server side logic for us. Mar 9, 11:04 AM Eric Jahn Sandeep Dolia We can later on cut this HMISService jar into smaller pieces Sandeep • Mar 9, 11:04 AM Surya Yadavalli Sandeep, factory approach will simplify things rather. If you look at it, we will have better control and abstract things in the right way (look at how inter and intra service communication can be handled using factory) . Its NOT technically challenging to implement. No additional over head to the system. Not only from technical stand point, from development stand point , it will simplify things. Surya • Mar 9, 11:05 AM Sandeep Dolia But the core pieces like Enrollment, Clients etc should be part of HMISService. Even if we slit the jars we would need all the jar files either ways for the factory approach. Because even the factory approach gets the beans from the Context. Sandeep • Mar 9, 11:07 AM I'm just thinking about over time, how each component could get pretty large. For this project, not so much, but over time it will, as we get new users with new requirements. Mar 9, 11:07 AM Eric Jahn Sandeep Dolia Agreed. And we would find a way to cut out a different jar based on a function point or business feature. Sandeep • Mar 9, 11:08 AM Surya Yadavalli The kind of design we are leaning into towards micro services, will give us the flexibility to have the service layers be different as well. We already solved that problem with that. Just think Notification Service and ENrollment service. Both business components can happily live independently. That is what we have in the diagrams I posted today. In that example, Notification Business component is not actually sitting together with other business components. Surya • Mar 9, 11:10 AM Sandeep Dolia Surya they are 2 services which would need to consume all the required spring beans. We are talking about the @Service layer Sandeep • Mar 9, 11:11 AM Surya Yadavalli I am talking about @Service layer as well. Surya • Mar 9, 11:11 AM Sandeep Dolia A factory also requires beans to be in the spring context. Sandeep • Mar 9, 11:12 AM Surya Yadavalli I guess, you now see why we need a factory. Surya • Mar 9, 11:12 AM Sandeep is correct about the limited number of services which could currently consume all the required Spring beans, but this could change. Okay, let's go with the Factory approach. I really value Sandeep's urges to keep it simple, though. It's very easy to go crazy with abstraction, and he's forcing us to justify it. And we may need to go back to Sandeep's approach if it gets too complicated. Mar 9, 11:12 AM Eric Jahn ... Sandeep Dolia Or how about this. Could we just make things very simple and using Spring DI. And if we run into a complex scenario then we can bring in the factory task This way we don't have to change a lot. I'm open to both the approaches From an agile perspective We are not blocked if we don't implement Factory approach. Sandeep • Mar 9, 11:23 AM Surya Yadavalli Factory, I don't see it as luxury, I see it as a need for our application Surya • Mar 9, 11:24 AM I just don't see how the DI approach alone can easily handle very limited microservices which can be dynamically distributed on different server nodes on installation or runtime. It seems like it requires two different code statement to handle the two different communication approaches (REST or direct calls). Using DI alone forces us to cluster services in a specific way, which we can't be sure of. Mar 9, 11:26 AM Eric Jahn Surya, one thing about your diagram at https://github.com/servinglynk/OpenHMISDataWarehouse/wiki/Communication-between-Layers-%28Factory-Approach%29---Design Mar 9, 11:30 AM Eric Jahn You don't have any lines connecting the microservices to each other RESTfully. But that could happen, right? Mar 9, 11:31 AM Eric Jahn Surya Yadavalli Yes, that is the Job of NOTIFICATION CLIENT COMPONENT (in the diagram) - for the purpose of this demonstration, I didn't detail that. If you look at it all others are named as "Business Components" and Notification is named as "Client Component", which calls Notification Micro service "REST fully ". I will modify the diagram, so it doesn't mislead Surya • Mar 9, 11:33 AM So wouldn't there be a line (in the Factory section), pointing from the Notification Client Component to the Service Factory, for when incoming REST messages arrive to the Notification Client? Not that you'd need to change it, but just so I understand... Mar 9, 11:35 AM Eric Jahn Sandeep Dolia Surya I see your diagrams and also won't all the @service layer will be in the spring context ? So accordingly to both the diagrams we would have all the @service layer beans all the clients in their Containers In other words Enrollment Service endpoint will have all the @Service layer beans in its Spring container. Sandeep • Mar 9, 11:39 AM That doesn't sound very "micro". Mar 9, 11:39 AM Eric Jahn Sandeep Dolia Eric, this would answer your question about a @Service bean NOT being available for a Microservice; That's what the diagram says. Surya please correct me if I'm wrong. The area where Factory would be a great use here would be. To not declare an @Service layer. And just have interfaces and thier implementations. And make them a spring bean with in each Micor Services spring context Sandeep • Mar 9, 11:42 AM ... Sandeep Dolia i.e Each micro service can have an application context file which will contain entries of those beans which it really wants. Sandeep • Mar 9, 11:44 AM I think I get what he's asking. The Factory section of the diagram makes it appear as though each RESTful service (like Enrollment) has *all* the business components available to it. Is that right, Sandeep? But it's just the diagram that makes it appear this way, not how it would really be. Mar 9, 11:46 AM Eric Jahn Surya, can you please confirm if all the @Service layer beans will be in the Spring context for both the approaches ? Based on our discussion last week you indicated that we will be having all the spring beans in the spring container Eric, I'm trying to clarify your point with Surya. Sandeep • Mar 9, 11:49 AM Surya Yadavalli Unless,we want a service (like notification service) that need to be / can be independent . The current diagram has that flaw, where in it doesn't show Notification business service in a separate service layer . All HMIS business layer components, can be part of a JAR and they are added as a dependency jar to - any micro service we develop. Surya • Mar 9, 11:54 AM but the goal would be to minimize what is in/available to each microservice, right? Mar 9, 11:55 AM Eric Jahn Surya Yadavalli Otherwise, business services specific to a microservice can be part of that mciroservice , if we are looking at that MICRO. Yes, Surya • Mar 9, 11:56 AM Sandeep Dolia Surya Thanks for answering that question. My 2 cents. A better idea like I mentioned earlier would be not to have @Service layer Sandeep • Mar 9, 11:57 AM Surya Yadavalli from a service Level, you can always, take a service and deploy it some where else, Surya • Mar 9, 11:58 AM Sandeep Dolia just have an interface and implementation and we can declare them as beans with in individual microservices i.e with in SpringApplicationContext.xml file for each micro service. This way the Service layer will be a plug and play layer. Sandeep • Mar 9, 12:00 PM Surya Yadavalli So, Eric - idea behind the jar file with all HMIS business components is like this.This jar is added just as a dependency to the Micro service just like we include several jars in our applications (like spring jars, hibernate jars, JDK librararies) not necssarily using every class from the library Surya • Mar 9, 12:01 PM Surya, there wouldn't be any specific service layer that would have all of the components in it? The jar dependency would just be minimal. The diagram just has them all there just to show the possibilities. Each microservice would only have a subset of the components it required. Do I have this right? Mar 9, 12:01 PM Eric Jahn Surya Yadavalli
- Each microservice would only have a subset of the components it required. Do I have this right? - We could, but No need to have a subset of business components it requires. It can just have the one "service layer jar" that has all HMIS related business components. However, that jar will not have anything related to "Notification business layer components" - because that is altogether an independent module (just an example). Whether we use or not all business components can be part of a single "jar". Enrollment Micro service, will talk to its business components from the same JAR, Client Micro service , will talk to its business components, from the same JAR - However, when both of them need to talk to NOTIFICAITON, they go via end point, those notification business components are not necessarily present in this JAR. Also, If for some business reason, assume that Enrollment Business component needs to talk to Client Business component - It will be accomplished by calling factory.getClientService() - Its factory that decides how the call need to be made - it can give a reference of service which is available in the same JAR or you can give a reference of its Client - that makes a REST call to Client service (it depends on load, traffic, or we want that call to be made via REST). So, only from "build" stand point, we can have all business components in a same JAR.