Feature Flags - Should they be exposed to client applications? - flags

I'm considering using Feature Flags in a web based app that has both javascript/html and mobile native clients, and am trying to make an informed decision on the following:
Should feature flags be exposed to client applications?
When discussing this with others, 2 approaches have appeared with how clients deal with feature flags, those being:
1) Clients know nothing about feature flags at all.
Server side endpoints that respond with data would include extra data to say if a feature was on or off.
e.g. for a fictional endpoint, /posts, data could be returned like so
enhanced ui feature enabled:
{
enhanced_ui: true,
[1,2,3,4,5]
}
enhanced ui feature disabled:
{
enhanced_ui: false,
[1,2,3,4,5]
}
2) Clients can access an endpoint, and ask for feature flag states.
e.g. /flagstates
{
'enhanced_ui:true
}
Clients then use this to hide or show features as required.
Some thoughts:
Approach #1 has less moving parts - no client side libraries are needed for implementing gates at all.
The question comes up though - when dynamic flags are updated, how do clients know? We can implement pub/sub to receive notifications and reload clients, then they'd automatically get the new up to date data.
Approach #2 feels like it might be easier to manage listening for flag updates, since it's a single endpoint that returns features, and state changes could be pushed out easily.

This is something that interested me as well as I have a requirement to implement feature flags/switches in the product I am working on. I have been researching this area for the past week and I will share my findings and thoughts (I am not claiming they are best practice in any way). These findings and thoughts will be heavily based on ASP.Net Zero and ASP.Net Boilerplate as I found these to be the closest match for an example implementation for what I am looking for.
Should feature flags be exposed to client applications?
Yes and no. If you are building a software as a service product (with multitenancy potentially), then you will most likely have to have some sort of management ui where admin users can manage (CRUD/Enable/Disable) features . This means that if you are building a SPA, you will obviously have to implement endpoints in your api (appropriately secured, of course) that your front end can use to retrieve details about the features and their current state for editing purposes. This could look like something below:
"features": [
{
"parentName": "string",
"name": "string",
"displayName": "string",
"description": "string",
"defaultValue": "string",
"inputType": {
"name": "string",
"attributes": {
"additionalProp1": {},
"additionalProp2": {},
"additionalProp3": {}
},
....
Model for features can, of course, vary based on your problem domain, but above should give you an idea of a generic model to hold feature definition.
Now as you can see, there is more to the feature than just a boolean flag whether it is enabled or not - it may have attributes around it. This is something that was not obvious at all for me to begin with as I only thought about my problem in the context of fairly simple features (true/false) where as actually, there may be features that are a lot more complex.
Lastly, when your users will be browsing your app, if you are rendering the UI for tenant who has your EnhancedUI feature enabled, you will need to know if the feature is enabled. In ASP.Net Zero this done by using something called IPermissionService, which is implemented in both, front end and back end. In back end, the permission service would basically check if the user is supposed to be allowed to access some resource, which in feature switch context means to check whether the feature is enabled for a given tenant. In front end (Angular), the permission service retrieves these permissions (
/api/services/app/Permission/GetAllPermissions):
{
"items": [
{
"level": 0,
"parentName": "string",
"name": "string",
"displayName": "string",
"description": "string",
"isGrantedByDefault": true
}
]
}
This can then be used to create some sort of RouteGuard where if something is not enabled or not allowed, you can appropriately redirect for example to an Upgrade your edition page.
Hopefully this gives you some ideas to think about.

Related

Domain Events vs Event notification vs Event-carried state transfer ECST in event driven architecture, implementing Domain Driven Design (DDD)

I would like to clarify in my mind the way different kinds of events could be implemented in a EDA system (system with Event Driven Architecture) implementing DDD (Domain Driven Design). Let assume that we are not using event sourcing.
More specifically having read relevant articles it seems there are 3 kinds of events:
Event notification: This kind of event seems not to carry much details, it just notifies the for the event which has happened, providing a way to query for more information.
"type": "paycheck-generated",
"event-id": "537ec7c2-d1a1-2005-8654-96aee1116b72",
"delivery-id": "05011927-a328-4860-a106-737b2929db4e",
"timestamp": 1615726445,
"payload": {
"employee-id": "456123",
"link": "/paychecks/456123/2021/01" }
}
Event-carried state transfer (ECST): This event seems to come in two flavours, either it has a delta of some information which was changed, or it contains all the relevant information (snapshot) for a resource.
{
"type": "customer-updated",
"event-id": "6b7ce6c6-8587-4e4f-924a-cec028000ce6",
"customer-id": "01b18d56-b79a-4873-ac99-3d9f767dbe61",
"timestamp": 1615728520,
"payload": {
"first-name": "Carolyn",
"last-name": "Hayes",
"phone": "555-1022",
"status": "follow-up-set",
"follow-up-date": "2021/05/08",
"birthday": "1982/04/05",
"version": 7
}
}
{
"type": "customer-updated",
"event-id": "6b7ce6c6-8587-4e4f-924a-cec028000ce6",
"customer-id": "01b18d56-b79a-4873-ac99-3d9f767dbe61",
"timestamp": 1615728520,
"payload": {
"status": "follow-up-set",
"follow-up-date": "2021/05/10",
"version": 8
}
}
Domain event: This event lies somewhere in between on both of the other two, it has more information than the event notification but this info is more relevant to a specific domain.
The examples above for each one from the Khononov's book (Learning Domain-Driven Design: Aligning Software Architecture and Business Strategy)
Having said the previous statements I would like to clarify the following questions:
(1) Is the typical use of the Event-carried state transfer (ECST) and Event notifications type of events to be used in the form of integration events (in a DDD EDA system) when communicating with other bounded contexts? (via transforming domain events to integration
events depending on the use case)
(2) Is there one or many other typical categories of events in a Domain Driven Designed system utilising Event Driven Architecture? For example: event notifications when domain errors occur - for client notification for the specific error (which in this case there is no aggregate persistence taking place) - how are these kind of errors are propagated back to the client, and what could be the name of such events accepted by the DDD EDA community?

How to hide json data from network tab

I use laravel and vue to menage some data from db, and i return json format from laravel controller to vue js. I just want to hide the response data from network tab or to mask them maybe. I didnt does this before. I mean, when i open network tab i see a request get-users?page=1 and if i double click open this urlhttp://127.0.0.1:8000/admin/users/get-users?page=1 witch show me all data like this
{
"data": [
{
"id": 1,
"name": "Admin",
"email": "admin#admin.com",
"email_verified_at": null,
"last_online_at": "2022-12-02 10:27:20",
is there any way to mask this data to somethink like this
"data": [
{
success: true,
response: null //or true
}
this is how i return users data
return new UserResource(User::paginate($paginate));
i want hide data from this tab
http://127.0.0.1:8000/admin/users/get-users?page=1
Requests will be shown.
This cannot be stopped, the application is making requests and this will be logged to the network tab by the browser, if there are security concenrns you should be handling this a different way. Do not send data to the client that they should not be allowed access to in the first place.
To try and ensure security run over HTTPS on the off chance to data gets intercepted, that way it will not be usable data. Most data will be provided by the user. Meaning in should not need to be hidden within the network tab.
Worst case scenario, someone physically sits at their computer and reads what is in the network tab, but this is a scenario that cant be accounted for when developing applications. You could base64 encode data that is being sent to and from so it is less readable to anyone who should see the network tab. Here are some resources to have a look through related to the question.
Base64

Best practice for subscribe and publish architecture for chat-like app

I'd like to know what best practices exist around subscribing to changes and publishing them to the user. This is a pretty broad and vaguely worded question. Therefore, allow me to elaborate on this using an example.
Imagine the following (simplified) chat-like application:
The user opens the app and sees the home screen.
On this home screen a list of chat-groups is fetched and displayed.
Each chat-group has a list of users (members).
The user can view this list of members.
Each user/member has at least a first name available.
The user can change its name in the settings.
And now the important part: When this name is changed, every user that is viewing the list of members, should see the name change in real-time.
My question concerns the very last point.
Let's create some very naive pseudo-code to simulate such a thing.
The client should at least subscribe to something. So we could write something like this:
subscribeToEvent("userChanged")
The backend should on its part, publish to this event with the right data. So something like this:
publishDataForEvent("userChanged", { userId: "9", name: "newname" } )
Of course there is a problem with this code. The subscribed user now gets all events for every user. Instead it should only receive events for users it is interested in (namely the list of members it is currently viewing).
Now that is the issue I want to know more about. I could think of a few solutions:
Method 1
The client subscribes to the event, and sends with it, the id of the group he is currently viewing. Like so for example:
subscribeToEvent("userChanged", { groupId: "abc" })
Consequently, on the backend, when a user changes its name, the following should happen:
Fetch all group ids of the user
Send out the event using those group ids
Something like this:
publishDataForEvent("userChanged", { userId: "9", name: "newname" }, { groupIds: ["abc", "def" })
Since the user is subscribed to a group with id "abc" and the backend publishes to several groups, including "abc", the user will receive the event.
A drawback of this method is that the backend should always fetch all group ids of the user that is being changed.
Method 2
Same as method 1. But instead of using groupIds, we will use userIds.
subscribeToEvent("userChanged", { myUserId: "1" })
Consequently, on the backend, when a user changes its name, the following should happen:
Fetch all the user ids that relate to the user (so e.g. friendIds based on the users he shares a group with)
Send out the event using those friendIds
Something like this:
publishDataForEvent("userChanged", { userId: "xyz", name: "newname" }, { friendIds: ["1", "2" })
An advantage of this is that the subscription can be somewhat more easily reused. Ergo the user does not need to start a separate subscription for each group he opens, since he is using his own userId instead of the groupId.
Drawback of this method is that it (like with method 1 but probably even worse) potentially requires a lot of ids to publish the event to.
Method 3
This one is just a little different.
In this method the client subscribes on multiple ids.
An example:
On the client side the application gathers all users that are relevant to the current user. For example, that can be done by gathering all the user ids of the currently viewed group.
subscribeToEvent("userChanged", { friendIds: ["9", "10"] })
At the backend the publish method can be fairly simple like so:
publishDataForEvent("userChanged", { userId: "9", name: "newname" }, { userId: "9" } )
Since the client is subscribed to user with userId "9", amongst several users, the client will receive this event.
Advantage of this method is that the backend publish method can be held fairly simple.
Drawback of this is that the client needs quite some logic to subscribe to the right users.
I hope that the examples made the question more clear. I have the feeling I am missing something here. Like, major chat-app companies, can't be doing it one of these ways right? I'd love to hear your opinion about this.
On a side note, I am using graphql as a backend. But I think this question is general enough to not let that play a role.
The user can change its name in the settings.
And now the important part: When this name is changed, every user that is viewing the list of members, should see the name change in real-time.
I assume the user can change his name via a FORM. The contents of that form will be send with a HTTP-Reuqest to a backand script that will do the change in a DB like
update <table> set field=? where userid=?
Preferred
This would be the point where that backend script would connect to your web socket server and send a message like.
{ opcode:'broadcast', task:'namechange', newname='otto' ,userid='47111' }
The server will the broadcast to all connected clients
{task:'namechange', newname='otto' ,userid='4711' }
All clients that have a relationship to userid='4711' can now take action.
Alternative 1
If you cant connect your backend script to the web socket server the client might send { opcode:'broadcast', task:'namechange', newname='otto' ,userid='47111' }
right before the FORM is trasnmitted to the backend script.
This is shaky because if anything goes wrong in the backend, the false message is already delivered, or the client might die before the message goes out, then no one will notice the change.

How to filter data by channel in config file of sync gate way in Couchbase?

I connected to sync gate way via Api ,but I don't know how to filter some data and use them in my laravel project.
You are not filtering by channel in the syncgateway config, the filtering is the outcome of the sync-function, but it is more of a passive result of attaching a channel to a document.
I have no idea which version you are using because your question lacks it, however configuration is pretty straight forward.
you basically have 2 options of attaching a channel to a document, the second is overriding the first:
1. don't have any sync function in the config file and just rely on a "channels" property, that property will make the document sync to the appropriate channels.
for example:
{ "name": "Duke", "lastName": "Nukem", "channels": ["DukeNukem","Duke"] }
2. Have a sync function in the config file:
For the document:
{ "name": "Duke", "lastName": "Nukem" }
you might have that sync function that will do the same:
function(doc, oldDoc){
if (doc.name && doc.lastName) {
channel(doc.name);
channel(doc.name + doc.lastName);
}
}
please note that you will have to grant permission to the user to able to see a channel.
in the client side you would need that user with the permission, and if you are not filtering for a channel - you will get the docs whenever you sync.
please read more here
Here is a Swift example on the client side on how to use the "channels" to route data:
let manager = CBLManager()
let database = try! manager.databaseNamed(dbName)
let pull = self.database.createPullReplication(url)
// We need to sync channels
pull.channels = ["somechannels"]
pull.start()
A concrete example on a Store management application, every documents belong to a Store would be saved with channels contain the storeID.
On the client side, when syncing we will put the storeID inside channels so that the sync will get only the documents belong to that Store. (we are using the default sync function)
Note that, there are security concern that you need to take into consideration, read more here.

Getstream.io: Message format for deleted activities via socket/faye

I am implementing something like Facebook reactions via getstream.io.
Posting and removing activities ("reactions") works fine.
Basics:
While implementing the socket feature (faye) of getstream to reflect feed changes in realtime, I saw that the format of a socket message for new activities differs from the one for deleted activities.
Example having ONE reaction each in deleted and new:
{
"deleted": [
"d5b1aee0-5a1a-11e6-8080-80015eb61bf9",
"49864f80-5a19-11e6-8080-80015eb61bf9",
"47fe7700-5a19-11e6-8080-80015eb61bf9",
"4759ab80-5a19-11e6-8080-80015eb61bf9",
"437ce680-5a19-11e6-8080-80015eb61bf9"
],
"new": [
{
"actor": "user:55d4ab8a11234359b18f06f6:Manuel Reil",
"verb": "support",
"object": "control:56bf2fb884e5c0756e910755",
"target": null,
"time": "2016-08-04T11:48:23.168000",
"foreign_id": "55d4ab8a11234359b18f06f6:support:56bf2fb884e5c0756e910755",
"id": "58d9c000-5a39-11e6-8080-80007c3c41d8",
"to": [],
"origin": "control:56bf2fb884e5c0756e910755"
}
],
"published_at": "2016-08-04T11:48:23.546708+00:00"
}
I subscribe to the aggregated feed that follows a flat feed.
I add and remove activities via the flat feed.
Subscriptions to the flat and the aggregated feed both return the same message when adding and removing activities.
Challenges I am facing:
When I remove ONE activity (via foreign_id) - why do appear 5 ids in the deleted array?
I need to have the foreign_id to reflect changes in the app while digesting the socket message from gestream.io. This works fine for new activities as the full object is sent (see example above). However, for the deleted activities they are missing as just an array of ids is sent.
Potential approaches:
Can I somehow configure my getstream faye subscription or config to (also) return foreign_idsfor the deleted items?
I could try to fetch those ids in addition based on the socket message, but this seems almost ridiculous.
Thank you very much.
Removing activities via foreign_id deletes all the activities with the given foreign_id present in the feed. This is one of the main upsides of using the foreign_id field, it allows for cascading deletes to a group of activities. (eg. Post and Likes is a typical use-case where you want to delete one Post and all Likes related to it).
Another advantage of using foreign_id is that you don't have to keep track of the ID generated by Stream.
You should be able to solve your first problem by picking a value for the foreign_id field that is unique (eg. the object ID from your database), this way you can still delete easily and avoid the cascaded delete behavior.
Regarding your second problem, if you are updating your UI based on the the real-time updates it also means you already read from the same feed, and that you have the list of activities with their IDs and foreign_ids. Selecting activities from activity_id should be just a matter of creating some sort of in memory map (eg. add an data-activity_id attribute to your DOM).

Resources