How can I combine database calls and REST calls into a single aggegrate? - api

I am currently developing an API which needs to "extend" its own data (my database) with the data I receive from another API.
I have an inventory class/aggregate in the domain layer with a repository interface which is implemented in the infrastructure layer.
Now I have injected via Dependency Injection both the Entity Manager for my own database as well as the RestClient for the external API.
public class RestInventoryRepository implements InventoryRepository {
#RestClient
#Inject
InventoryRestClient inventoryRestClient;
#Inject
EntityManager eM;
In the repository method implementation I first call the rest client and receive its representation of the requested inventory. I then map it to my inventory class with an Object Mapper. After that I try to get the additional information from my boxInventory by the same inventory id and then append it to the earlier received inventory.
The result is a rather big method which only gets bigger with other additions. My question now is if there is a good practise to handle situations like that? I found the API Composition pattern but I am not sure if I can handle mixing a database with a API the same way as if mixing different APIs.
public Optional<Inventory> findInventoryById(String inventoryId) {
JSONObject inventoryJSON = inventoryRestClient.getInventoryById(inventoryId);
if (inventoryJSON == null) {
return Optional.empty();
}
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Inventory inventory = objectMapper.convertValue(inventoryJSON, Inventory.class);
//extracts boxTagInventory
TypedQuery<BoxInventory> typedQuery =
eM.createQuery("FROM BoxInventory WHERE INVENTORY_ID = :inventoryId",
BoxInventory.class);
typedQuery.setParameter("inventoryId", inventory.inventoryId());
List<BoxInventory> resultSet = typedQuery.getResultList();
if(resultSet.isEmpty()){
return Optional.empty();
}
inventory.addBoxInventory(resultSet.get(0));
return Optional.of(inventory);
}

Related

Chaining Reactive Asynchronus calls in spring

I’m very new to the SpringReactor project.
Until now I've only used Mono from WebClient .bodyToMono() steps, and mostly block() those Mono's or .zip() multiple of them.
But this time I have a usecase where I need to asynchronously call methods in multiple service classes, and those multiple service classes are calling multiple backend api.
I understand Project Reactor doesn't provide asynchronous flow by default.
But we can make the publishing and/or subscribing on different thread and make code asynchronous
And that's what I am trying to do.
I tried to read the documentation here reactor reference but still not clear.
For the purpose of this question, I’m making up this imaginary scenario. that is a little closer to my use case.
Let's assume we need to get a search response from google for some texts searched under images.
Example Scenario
Let's have an endpoint in a Controller
This endpoint accepts the following object from request body
MultimediaSearchRequest{
Set<String> searchTexts; //many texts.
boolean isAddContent;
boolean isAddMetadata;
}
in the controller, I’ll break the above single request object into multiple objects of the below type.
MultimediaSingleSearchRequest{
String searchText;
boolean isAddContent;
boolean isAddMetadata;
}
This Controller talks to 3 Service classes.
Each of the service classes has a method searchSingleItem.
Each service class uses a few different backend Apis, but finally combines the results of those APIs responses into the same type of response class, let's call it MultimediaSearchResult.
class JpegSearchHandleService {
public MultimediaSearchResult searchSingleItem
(MultimediaSingleSearchRequest req){
return comboneAllImageData(
getNameApi(req),
getImageUrlApi(req),
getContentApi(req) //dont call if req.isAddContent false
)
}
}
class GifSearchHandleService {
public MultimediaSearchResult searchSingleItem
(MultimediaSingleSearchRequest req){
return comboneAllImageData(
getNameApi(req),
gitPartApi(req),
someRandomApi(req),
soemOtherRandomApi(req)
)
}
}
class VideoSearchHandleService {
public MultimediaSearchResult searchSingleItem
(MultimediaSingleSearchRequest req){
return comboneAllImageData(
getNameApi(req),
codecApi(req),
commentsApi(req),
anotherApi(req)
)
}
}
In the end, my controller returns the response as a List of MultimediaSearchResult
Class MultimediaSearchResponse{
List< MultimediaSearchResult> results;
}
If I want to use this all asynchronously using the project reactor. how to achieve it.
Like calling searchSingleItem method in each service for each searchText asynchronously.
Even within the services call each backend API asynchronously (I’m already using WebClient and converting response bodyToMono for backend API calls)
First, I will outline a solution for the upper "layer" of your scenario.
The code (a simple simulation of the scenario):
public class ChainingAsyncCallsInSpring {
public Mono<MultimediaSearchResponse> controllerEndpoint(MultimediaSearchRequest req) {
return Flux.fromIterable(req.getSearchTexts())
.map(searchText -> new MultimediaSingleSearchRequest(searchText, req.isAddContent(), req.isAddMetadata()))
.flatMap(multimediaSingleSearchRequest -> Flux.merge(
classOneSearchSingleItem(multimediaSingleSearchRequest),
classTwoSearchSingleItem(multimediaSingleSearchRequest),
classThreeSearchSingleItem(multimediaSingleSearchRequest)
))
.collectList()
.map(MultimediaSearchResponse::new);
}
private Mono<MultimediaSearchResult> classOneSearchSingleItem(MultimediaSingleSearchRequest req) {
return Mono.just(new MultimediaSearchResult("1"));
}
private Mono<MultimediaSearchResult> classTwoSearchSingleItem(MultimediaSingleSearchRequest req) {
return Mono.just(new MultimediaSearchResult("2"));
}
private Mono<MultimediaSearchResult> classThreeSearchSingleItem(MultimediaSingleSearchRequest req) {
return Mono.just(new MultimediaSearchResult("3"));
}
}
Now, some rationale.
In the controllerEndpoint() function, first we create a Flux that will emit every single searchText from the request. We map these to MultimediaSingleSearchRequest objects, so that the services can consume them with the additional metadata that was provided with the original request.
Then, Flux::flatMap the created MultimediaSingleSearchRequest objects into a merged Flux, which (as opposed to Flux::concat) ensures that all three publishers are subscribed to eagerly i.e. they don't wait for one another. It works best on this exact scenario, when several independent publishers need to be subscribed to at the same time and their order is not important.
After the flat map, at this point, we have a Flux<MultimediaSearchResult>.
We continue with Flux::collectList, thus collecting the emitted values from all publishers (we could also use Flux::reduceWith here).
As a result, we now have a Mono<List<MultimediaSearchResult>>, which can easily be mapped to a Mono<MultimediaSearchResponse>.
The results list of the MultimediaSearchResponse will have 3 items for each searchText in the original request.
Hope this was helpful!
Edit
Extending the answer with a point of view from the service classes as well. Assuming that each inner (optionally skipped) call returns a different type of result, this would be one way of going about it:
public class MultimediaSearchResult {
private Details details;
private ContentDetails content;
private MetadataDetails metadata;
}
public Mono<MultimediaSearchResult> classOneSearchSingleItem(MultimediaSingleSearchRequest req) {
return Mono.zip(getSomeDetails(req), getContentDetails(req), getMetadataDetails(req))
.map(tuple3 -> new MultimediaSearchResult(
tuple3.getT1(),
tuple3.getT2().orElse(null),
tuple3.getT3().orElse(null)
)
);
}
// Always wanted
private Mono<Details> getSomeDetails(MultimediaSingleSearchRequest req) {
return Mono.just(new Details("details")); // api call etc.
}
// Wanted if isAddContent is true
private Mono<Optional<ContentDetails>> getContentDetails(MultimediaSingleSearchRequest req) {
return req.isAddContent()
? Mono.just(Optional.of(new ContentDetails("content-details"))) // api call etc.
: Mono.just(Optional.empty());
}
// Wanted if isAddMetadata is true
private Mono<Optional<MetadataDetails>> getMetadataDetails(MultimediaSingleSearchRequest req) {
return req.isAddMetadata()
? Mono.just(Optional.of(new MetadataDetails("metadata-details"))) // api call etc.
: Mono.just(Optional.empty());
}
Optionals are used for the requests that might be skipped, since Mono::zip will fail if either of the zipped publishers emit an empty value.
If the results of each inner call extend the same base class or are the same wrapped return type, then the original answer applies as to how they can be combined (Flux::merge etc.)

Spring Webflux send event when any new data

I'm trying to learn Spring webflux & R2DBC. The one I try is simple use case:
have a book table
create an API (/books) that provides text stream and returning Flux<Book>
I'm hoping when I hit /books once, keep my browser open, and any new data inserted to book table, it will send the new data to browser.
Scenario 2, still from book table:
have a book table
create an API (/books/count) that returning count of data in book as Mono<Long>
I'm hoping when I hit /books/count once, keep my browser open, and any new data inserted /deleted to book table, it will send the new count to browser.
But it does not works. After I isnsert new data, no data sent to any of my endpoint.
I need to hit /books or /books/count to get the updated data.
I think to do this, I need to use Server Sent Events? But how to do this in and also querying data? Most sample I got is simple SSE that sends string every certain interval.
Any sample to do this?
Here is my BookApi.java
#RestController
#RequestMapping(value = "/books")
public class BookApi {
private final BookRepository bookRepository;
public BookApi(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
#GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Book> getAllBooks() {
return bookRepository.findAll();
}
#GetMapping(value = "/count", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Mono<Long> count() {
return bookRepository.count();
}
}
BookRepository.java (R2DBC)
import org.springframework.data.r2dbc.repository.R2dbcRepository;
public interface BookRepository extends R2dbcRepository<Book, Long> {
}
Book.java
#Table("book")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Book {
#Id
private Long id;
#Column(value = "name")
private String name;
#Column(value = "author")
private String author;
}
Use a Processor or Sink to handle the Book created event.
Check my example using reactor Sinks, and read this article for the details.
Or use a tailable Mongo document.
A tailable MongoDB document can do the work automatically, check the main branch of the same repos.
My above example used the WebSocket protocol, it is easy to switch to SSE, RSocket.
Below Post would help you to achieve your first requirement
Spring WebFlux (Flux): how to publish dynamically
Let me know , if that helps you

Advice data acces pattern without repository

I want to access ISession directly from application services (without using repository (http://ayende.com/blog/3955/repository-is-the-new-singleton)) but application services unit test is hard and Nhibernate data access code increase complexity of code (no repository mock and I don't want to mock repository or in memory db like sqllite for testing)
Is there any efficient way access to ISession from service layer?
I use a sessionHelper which basically holds the ISessionFactory - and then have methods like this:
public T WrapQueryInTransaction<T>(Func<ISession, T> query)
{
using (var tx = Session.BeginTransaction())
{
try
{
var result = query(Session);
tx.Commit();
return result;
}
}
}
I then have similar methods for common functionality - ie. xxxUpdaters which basically loads the object in question, makes the updates and then closes the ISession again.
And then usage is as follows for queries:
var entities = _sessionHelper.WrapQueryInTransaction(s => s.QueryOver<SomeEntity>().List());
For complex queries I have these incapsulated in a query class which can also be thrown at the sessionHelper.
It works for me - hope you can use it.

Per- request based Object Context and Complied Query Performance with Entity Framework

I'm currently working with WCF services application and we've created per request based Object Context of Entity framework. In the entity framework queries, we've used Complied Query mechanism, however, expected performance could not be achieved at the moment. I suspect it is due to the nature of Object Context(Per request based),as Complied queried depends on Object Context. Is it so?
Code Sample
private static readonly Func<MyContext, IQueryable<Order>> _compiledObjectQuery = CompiledQuery.Compile<MyContext, IQueryable<Order>>(
(ctx) => from Order in ctx.Orders
.Include("OrderType")
.Include("OrderLines")
select Order
);
protected override IQueryable<Order> OrderQuery
{
get { return _compiledObjectQuery.Invoke(Context); }
}
Context Creating
public OPDbContext DbContext
{
get
{
if(_dbConext == null)
{
_dbConext = new OPDbContext(Context, true);
}
return _dbConext;
}
}
Castle is used to inject Object Context per request base

With NHibernate, how can I create an INHibernateProxy?

After lots of reading about serialization, I've decided to try to create DTOs. After more reading, I decided to use AutoMapper.
What I would like to do is transform the parent (easy enough) and transform the entity properties if they've been initialized, which I've done with ValueResolvers like below (I may try to make it generic once I get it fully working). This part works.
public class OrderItemResolver : ValueResolver<Order, OrderItem>
{
protected override OrderItem ResolveCore(Order source)
{
// could also use NHibernateUtil.IsInitialized(source.OrderItem)
if (source.OrderItem is NHibernate.Proxy.INHibernateProxy)
return null;
else
return source.OrderItem;
}
}
}
When I transform the DTO back to an entity, for the entities that weren't initialized, I want to create a proxy so that if the entity wants to access it, it can. However, I can't figure out how to create a proxy. I'm using Castle if that's relevant.
I've tried a bunch of things with no luck. The below code is a mess, mainly because I've been trying things at random without knowing what I should be doing. Anybody have any suggestions?
public class OrderItemDTOResolver : ValueResolver<OrderDTO, OrderItem>
{
protected override OrderItem ResolveCore(OrderDTO source)
{
if (source.OrderItem == null)
{
//OrderItem OrderItem = new ProxyGenerator().CreateClassProxy<OrderItem>(); // Castle.Core.Interceptor.
//OrderItem OrderItem = new ProxyGenerator().CreateClassProxy<OrderItem>();
//OrderItem.Id = source.OrderItemId;
//OrderItem OrderItem = new OrderItem();
//var proxy = new OrderItem() as INHibernateProxy;
//var proxy = OrderItem as INHibernateProxy;
//return (OrderItem)proxy.HibernateLazyInitializer
//ILazyInitializer proxy = new LazyInitializer("OrderItem", OrderItem, source.OrderItemId, null, null, null, null);
//return (OrderItem)proxy;
//return (OrderItem)proxy.HibernateLazyInitializer.GetImplementation();
//return OrderItem;
IProxyTargetAccessor proxy = new Castle.Core.Interceptor.
var initializer = new LazyInitializer("OrderItem", typeof(OrderItem), source.OrderItemId, null, null, null, null);
//var proxyFactory = new SerializableProxyFactory{Interfaces = Interfaces, TargetSource = initializer, ProxyTargetType = IsClassProxy};
//proxyFactory.AddAdvice(initializer);
//object proxyInstance = proxyFactory.GetProxy();
//return (INHibernateProxy) proxyInstance;
return null;
//OrderItem.Id = source.OrderItemId;
//return OrderItem;
}
else
return OrderItemDTO.Unmap(source.OrderItem);
}
}
Thanks,
Eric
Maybe I over complicated it. This seems to work. Anybody see any issues with it?
public class OrderItemDTOResolver : ValueResolver<OrderDTO, OrderItem>
{
protected override OrderItem ResolveCore(OrderDTO source)
{
if (source.OrderItem == null)
return NHibernateSessionManager.Instance.Session.GetISession().Load<OrderItem>(source.AgencyId);
else
return OrderItemDTO.Unmap(source.OrderItem);
}
}
This may be one of those cases where the answer is "don't", or at least "you probably shouldn't". If you're mapping DTOs into NHibernate mapped objects directly you're not really using the mapped objects as domain objects, just as a fancy way to push data in and out of the database. This of course may be all you're after but having done this myself in the past I've found that it's problematic trying to use the same DTO data format in both directions. If you're going cross-process you've turned the service into a (difficult to maintain) CRUD layer. If you're in the same process you're doing unnecessary data shuffling with DTOs.
Sending DTOs out is fine, but consider projecting the data into a format more closely aligned with what the client actually needs. What you get back is better expressed in specific DTOs that express only the data needed to perform the actual action (Command objects, essentially). With a few automatic properties they're trivial to construct. You can then have a business method that performs the necessary action with only the necessary information, and that in a format suited to the action being performed. My primary use of AutoMapper (which does rock) these days is to translate incoming DTOs into types that domain methods can consume.
Also, public setters on mapped objects are undesirable because they allow the object to be manipulated by any code without any validation. This means any modification to them can leave them in an invalid state.
If you don't really care about the above (and it's not always applicable) the way you load individual instances does leave you open do doing many individual database loads which is a potential performance issue.