From React-Apollo Docs: Updating the Store
Apollo performs two important core tasks: Executing queries and
mutations, and caching the results.
Currently, in my App, I'm using Apollo to execute Queries and Mutations only.
The results of my queries and mutations are dispatched to a custom Redux reducer which my client then reads (writes, renders) from.
Are there distinct disadvantages of this current approach over using the Apollo Client's internal Redux store appropriately? (beyond writing additional code for the custom Redux store)
I care about disadvantages related to Design and Performance, favoring Design at the moment - with concern for if this pattern effectively "locks in" substandard performance, with future migration to the Apollo Store untenable for a medium sized application.
Thank you very much.
Related
I mainly have two questions. I haven't read this anywhere but, I am wondering whether or not it is a good idea to make it so that all the data that is going in and out of all apps in your project solely depended on REST API calls.
So that if you, for instance, want to register a new user. Gather the data from a front-end, with no back-end work, and just send this data as a REST call to you "registration-app" where all validation and back-end work is done.
I find this method effective when working in big teams as it makes dependencies even more decoupled, as well as making each part of the project more separated and "clear". My question is, therefore, is this a viable way of developing? Are there any security or performance issues with this? And where can I read more?
Thanks
Max
It is perfectly viable, I think like most choices it has pros and cons. Here are some of them:
Pros:
Decoupling - Clients depend on the abstraction (i.e. the REST API) rather than the concretion (i.e. the website), so you gain clarity of design, ability to test outside of the browser, and you can do things like substitute the REST API with different implementations e.g. with a mock service for development/testing purposes. If, in addition, the REST API is implemented by a separate back-end service, then you can update it independently, and potentially scale it independently.
Responsive user-interface - The REST requests can avoid HTML page reloads and improve UX. Also you can make asynchronous REST calls.
Reduced payload - Typically the REST calls would return less data than the HTML sent in a page refresh.
Cons:
More complex client - You require more complex javascript and especially so if you employ asynchronous REST calls.
Dynamic page building - Typically the result of a REST call might require some change in the UI, you are forced to do this dynamically in javascript which also adds complication. So your UI logic is split between your HTML page templates and your javascript UI updates. This makes the UI hard to reason about.
Timeouts - You need to handle timeouts and errors in javascript
Sessions - You need some means of authenticating users and maintaining sessions. REST services should not maintain client-session state themselves, so you either need to store state in the client, or explicitly add state as a new REST resource with its own distinct URI(s).
Forced page reload - If you use this mechanism to avoid page reloads, then users potentially might have the page open for a significant period of time, and you might need some kind of mechanism to cause them to reload it.
I'd like to use the microservices architectural pattern for a new system, but I'm having trouble figuring out how to share and merge data between the services when the services are isolated from each other. In particular, I'm thinking of returning consolidated data to populate a web app UI over HTTP.
For context, I'm intending to deploy each service to its own isolated environment (Heroku) where I won't be able to communicate internally between services (e.g. via //localhost:PORT. I plan to use RabbitMQ for inter-service communication, and Postgres for the database.
The decoupling of services makes sense for CREATE operations:
Authenticated user with UserId submits 'Join group' webform on the frontend
A new GroupJoinRequest including the UserId is added to the RabbitMQ queue
The Groups service picks up the event and processes it, referencing the user's UserId
However, READ operations are much harder if I want to merge data across tables/schemas. Let's say I want to get details for all the users in a certain group. In a monolithic design, I'd just do a SQL JOIN across the Users and the Groups tables, but that loses the isolation benefits of microservices.
My options seem to be as follows:
Database per service, public API per service
To view all the Users in a Group, a site visitor gets a list of UserIDs associated with a group from the Groups service, then queries the Users service separately to get their names.
Pros:
very clear separation of concerns
each service is entirely responsible for its own data
Cons:
requires multiple HTTP requests
a lot of postprocessing has to be done client-side
multiple SQL queries can't be optimized
Database-per-service, services share data over HTTP, single public API
A public API server handles request endpoints. Application logic in the API server makes requests to each service over a HTTP channel that is only accessible to other services in the system.
Pros:
good separation of concerns
each service is responsible for an API contract but can do whatever it wants with schema and data store, so long as API responses don't change
Cons:
non-performant
HTTP seems a weird transport mechanism to be using for internal comms
ends up exposing multiple services to the public internet (even if they're notionally locked down), so security threats grow from greater attack surface
Database-per-service, services share data through message broker
Given I've already got RabbitMQ running, I could just use it to queue requests for data and then to send the data itself. So for example:
client requests all Users in a Group
the public API service sends a GetUsersInGroup event with a RequestID
the Groups service picks this up, and adds the UserIDs to the queue
The `Users service picks this up, and adds the User data onto the queue
the API service listens for events with the RequestID, waits for the responses, merges the data into the correct format, and sends back to the client
Pros:
Using existing infrastructure
good decoupling
inter-service requests remain internal (no public APIs)
Cons:
Multiple SQL queries
Lots of data processing at the application layer
harder to reason about
Seems strange to pass large quantities around data via event system
Latency?
Services share a database, separated by schema, other services read from VIEWs
Services are isolated into database schemas. Schemas can only be written to by their respective services. Services expose a SQL VIEW layer on their schemas that can be queried by other services.
The VIEW functions as an API contract; even if the underlying schema or service application logic changes, the VIEW exposes the same data, so that
Pros:
Presumably much more performant (single SQL query can get all relevant data)
Foreign key management much easier
Less infrastructure to maintain
Easier to run reports that span multiple services
Cons:
tighter coupling between services
breaks the idea of fundamentally atomic services that don't know about each other
adds a monolithic component (database) that may be hard to scale (in contrast to atomic services which can scale databases independently as required)
Locks all services into using the same system of record (Postgres might not be the best database for all services)
I'm leaning towards the last option, but would appreciate any thoughts on other approaches.
To evaluate the pros and cons I think you should focus on what microservices architecture is aiming to achieve. In my opinion Microservices is architectural style aiming to build loosely couple applications. It is not designed to build high performance application so scarification of performance and data redundancy are something we are ready accept when we decided to build applications in a microservices way.
I don't think you services should share database. Tighter coupling scarify the main objective of the microservices architecture. My suggestion is to create a consolidated data service which pick up the data changes events from all the other services and update the database behind it. You might want to design the database behind the consolidated data service in a way that is optimised for query (like a data warehouse) because that's all this service will be used for. You might want to consider using a NoSQL database to support your consolidated data service.
We have an internal application. As time went on and new applications were requested, that exchange data between eachother, the interaction became bound to the database schema. Meaning changes in the database require changes everywhere else. As we plan to build even more applications that will depend on the same data this quickly will become and unmanagable mess.
Now i'm looking to abstract that interaction behind an API. Currently i have trouble choosing the right tool.
Interaction at times could be complex, meaning data is posted to one service and if the action has been completed it should notify the sender of that.
Another example would be that some data does not have context without the data from other services. Lets say there is one service for [Schools] and one for [Students]. So if the [School] gets deleted or changed the [Student] needs to be informed about it immeadetly and not when he comes to [School].
Advice? Suggestions? SOAP/REST/?
I don't think you need an API. In my opinion you need an architecture which decouples your database from the domain logic and other parts of the application. Such an architecture is for example clean architecture, onion architecture and hexagonal architecture (ports&adapters by new name). They share the same concepts, you have a domain logic, which does not depend from any framework, external lib, delivery method, data storage solutions, etc... This domain logic communicates with the outside world through adapters having well defined interfaces. If you first design the inside of your domain logic, and the interfaces of the adapters, and just after the outside components, then it is called domain driven design (DDD).
So for example if you want to move from MySQL to MongoDB you already have a DataStorageInterface, and the only thing you need is writing a MongoDBAdapter which implements this interface, and ofc migrate the data...
To design the adapters you can use two additional concepts; command and query segregation (CQRS) and event sourcing (ES). CQRS is for connecting delivery methods like REST, SOAP, webapplications, etc... to the domain logic. For example you can raise a CreateUserCommand from your REST API. After that the proper listener in the domain logic processes that command, and by success it raises a domain event, like UserCreatedEvent. Your REST API can listen to that event and respond with a success message to the REST client. The UserCreatedEvent can be listened by one or more storage adapter too. So they can process that event and persist the new user. You don't necessary use only a single database. For example if a relational database is faster by a specific type of query, then you can use that, but if a noSQL database suites better to the job, then you can use that too. So you can use as many databases as you want for your queries, the only thing you need is writing a storage adapter for them. For example if your REST client wants to retrieve the profile of a specific user, then it can raise a GetUserProfileByIdQuery and the domain logic can ask the adapter of a database which can serve the query. After that the adapter can send for example an SQL query to a MySQL database and return the response. By ES you add EventStorage to your system, which stores the raised domain events. It can be very useful if you want to migrate your data from one query database to another. In that case you create a new storage adapter to your new database, and replay all of the domain events from the EventStorage in historical order to that adapter, so it can fill the new database with the relevant data. That's all, you don't have to write complicated migration scripts...
In your case I think your should create at least domain events, and use event sourcing. That will totally decouple your database from the other parts of your application. Adding a REST or SOAP API can have a similar effect, but building HTTP connections to access your database can slow down your application.
We have 3 layered application where every call from the service layer goes to the business layer and is persisted by the data layer. Components of each layer can only call the layer below;
However because we have hundreds of entities and we have lots of services related to crud operations so many controversies raised on our team.
Some believe for the sake of maintenance and ease of development it's better to call data access from crud services which just doing crud operation and bypassing business layer.
On the contrary some saying we have to create wrapper for data access of each entity in business layer and call these wrapper from services and never allow services to call data access layer.
In your idea which way we should take? Is it ok for crud services to call data accesses and bypassing business layer?
If there is no business logic to perform, there is no reason to enforce a business layer. The 3-tier architecture is not an arcane protocol, just a best practice that was formed assuming business processing.
In a current application we are often accessing DAOs directly from JSF controllers when there is no business process involved. The idea was given by a java champion who stressed the idea that simplicity is paramount.
If you are worried about future modifications that may require adding business logic. I think of the issue this way: The additional business logic would be added to the business layer anyway, including data access, so there is no difference here.
CRUD code is mostly very simple. So the change in the service would amount to reroute a single call or a couple of calls fron the DAO to an EJB - a simple refactoring. The CRUD code itself would still remain, but will be pushed into the EJB - another simple refactoring.
This is not perfect, but IMO better then the alternative: having an empty indirection layer. This adds complexity that serves no purpose. The business object would do nothing but forward the calls to the DAO.
There are two code smells that I think apply in this case: contrived complexity and feature envy.
I am not saying that DA in the business layer is somehow a code smell. What I mean is that having a business object that does nothing else but proxy the DAO is a smell. It's the same with complexity - an added data structure / architectural layer that serves no own purpose - there seems to be a DAL in your application already.
Another aspect for your consideration would be - how surprising is it for a developer to see a service that uses a DAO directly? Having 5 services where 2 of them access DAO directly is different from having 100 services where only one service accesses the DAOs directly.
In the first case the code simplicity would outweigh the added conceptual complexity (2 concepts for a single thing), in the second case, I would rather stick with the business layer - the surprise (also called the WTF-effect ;) of doing it differently just once would be too big.
In my opinion, having call CRUD services bypassing the business layer will start converting the current service layer into a business layer as you increase the functionality. So your service layer will act as the business layer too, if you are fine with it.
In most cases, you deal with an entity and probably that could involve many data layer crud calls, on one update for example. A business layer is recommended for this purpose. And business layer is the place to execute any business rules, caching or call other business services if required. This will keep the upper layer simple and pass through.
I will not bypass the business layer. Even though it might look like you are only proxying the call for the DAL into the BL, and even though this may be a case of very simple CRUD operation, the BL can encapsulate the validation logic required for the operation. Validation logic can be performed on the BL and proxying to DAL will only be performed if the validation conditions are met.
If you only have CRUD operations, you can use Data Services to publish your data sets as web services. A good example to this is WSO2 Data Services http://wso2.com/products/data-services-server/
We have an iOS app that talks to a django server via REST API. Most of the data consists of rather large Item objects that involve a few related models that render into single flat dictionary, and this data changes rarely.
We've found, that querying this is not a problem for Postgres, but generating JSON responses takes a noticeable amount of time. On the other hand, item collections vary per-user.
I thought about a rendering system, where we just build a dictionary for Item object and save it into redis as JSON string, this way we can serve API directly from redis (e.g. HMGET(id of items in user library), which is fast, and makes it relatively easy to regenerate "rendered instances", basically just a couple of post_save signals.
I wonder how good this design is, are there any major flaws in it? Maybe there's a better way for the task?
Sure, we do the same at our firm, using Redis to store not JSON but large XML strings which are generated from backend databases for RESTful requests, and it saves lots of network hops and overhead.
A few things to keep in mind if this is the first time you're using Redis...
Dedicated Redis Server
Redis is single-threaded and should be deployed on a dedicated server with sufficient CPU power. Don't make the mistake of deploying it on your app or database server.
High Availability
Set up Redis with Master/Slave replication for high availability. I know there's been lots of progress with Redis cluster, so you may want to check on that too for HA.
Cache Hit/Miss
When checking Redis for a cache "hit", if the connection is dead or any exception occurs, don't fail the request, just default to the database; caching should always be 'best effort' since the database can always be used as a last resort.