I'm using rooms at the moment however I think the way I'm going about using them isn't practical. This is a portion of my code.
// Add clients that are to be notified to the users room.
for(var c in clients)
{
if(_.indexOf(data.notify, clients[c].id) != -1)
{
clients[c].socket.join(data.user_id);
}
}
// Emit to all that are now in the room.
io.sockets.in(data.user_id).emit('notification', data);
// Remove everyone from this users room so it's free for the next notification.
for(var c in clients)
{
if(clients[c].id in data.notify)
{
clients[c].socket.leave(data.user_id);
}
}
So as you can see I add the correct clients to a room which is an ID of a user who has updated something. Then once I emit the notification to the group I remove all the clients once more from the room to keep it free.
Is there a better way of going about this?
Related
I'm trying to unsubscribe a socket and make it leave the room he is in. I know his socket.id, to make you understand better, when the creator of the room leaves, a specific socket/all sockets should leave. Ty!
To leave a room, you use this:
socket.leave(roomName);
If you only have the socket.id for the socket, then you can get the socket that corresponds to that id with:
let socket = io.sockets.connected[id];
socket.leave(roomName);
If you want to clear everyone out of a particular room, you can do that like this:
function clearRoom(room, namespace = '/') {
let roomObj = io.nsps[namespace].adapter.rooms[room];
if (roomObj) {
// now kick everyone out of this room
Object.keys(roomObj.sockets).forEach(function(id) {
io.sockets.connected[id].leave(room);
})
}
}
All this code runs only on the server as rooms are a server-side concept only.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I am new to DDD and have a concern about the size of my Aggregate Root. The object graph is like the image below. (They are collections). The problem is all of the entities depend on the state of the AggregateRoot (Event). My question is: how do I break the aggregate into smaller aggregates? It's like I have a "God" like aggregate root that just manages everything.
This is very simplistic view of my domain:
and these are the rules:
An event has a number of different states. (implemented state design
pattern here).
An event has a collection of sessions. (but only 1 can be active at a time and only if the event is in the correct state).
A session has two states: Active and Ended.
A session has a collection of Guests.
A session has a collection of photos. (Maximum
of 10).
When a session is deleted. It should delete all its children.
When a session has ended and a photo is deleted it should
check to see if there are any other photos that belong to the
session. If not it should also delete the session.
When a session has ended and a photo is deleted sometimes it should throw an exception depending on the state of the event.
When a session is active and a photo is deleted. It should not worry about whether or not the session has any other photos or not.
When a session ends it must have at least 1 photo and at least 1 guest.
A photo can be updated but only if the event is in the right state.
When an event is deleted it should delete all its children.
Edit: I have divided the 1 aggregate into smaller aggregates so that Event, Session and Photo are all ARs. The issues is a session needs to perform a check on the Event AR before starting. Is it perfectly ok to inject an event object into the sessions start method Session.Start(Event #event) or will I have concurrency issues as outlined in some of the comments?
As a first step, the following 3 articles will be invaluable: http://dddcommunity.org/library/vernon_2011/
With DDD you are splitting the entities up in to boundaries where the state is valid after a single operation from an external source completes (i.e. a method call).
Think in terms of the business problem you are trying to solve - you have used the word delete a lot...
Does delete even have a place in the wording of the business experts for whom you are designing the system? Thinking in terms of the real world and not database infrastructure, unless you can create a time machine to travel back in time and stop an event from starting and therefore change history, the word delete has no real world analogy.
If you are forcing yourself to delete children on delete, that means that operation would need to become a transaction so things that may not make sense to sit inside the aggregate root are forced too (so that the state of the entity and all its children can be controlled and assured to be valid once the method call completes). Yes there are things where you can do with a transaction across multiple aggregate roots, but these are very rare situations and to be avoided if possible.
Eventual consistency is used as an alternative to transactions and reduce complexity, if you speak to the person for whom the system is being designed, you will probably find that a delay of seconds or minutes is more than acceptable. This is plenty of time to fire off an event, to which some other business logic is listening and takes necessary action. Using eventual consistency removes the headaches that come with transactions.
Photos could take up a lot of storage yes, so you would probably need a cleanup mechanism that runs after an event is marked as finished. I would probably fire off an event once the session is marked closed, a different system somewhere else would listen for this event and after 1 year (or whatever makes sense for you) remove this from a server... assuming you used an array of string[10] for your URLs.
If this is the maximum extent of your business logic, then don't only focus on DDD, it seems like this could be a good fit for Entity Framework which is essentially CRUD and has cascade deletes built in.
Edits answer
What is a photo, does it contain attributes? Is it not instead something like a Url to a photo, or a path to a picture file?
I'm not yet thinking of databases, that should be the very last thing that is thought of and the solution should be database/technology agnostic. I see the rules as:
An event has many sessions.
A Session has the following states: NotStarted, Started and Ended.
A Session has a collection of Guests, I'm going to assume these are unique (in that two guests with the same name are not the same, so a guest should be an aggregate root).
An Event has one active Session.
When there are no active Sessions, an Event can be marked as Finished.
No Sessions can be started once an Event is marked as Finished.
A session has a collection of up to 10 photos.
When a session has ended, a photo cannot be removed.
A Session can not start if there are no Guests A Session can not end if there are no Photos.
You cannot return the Session directly, as a user of your code may call Start() on the session, you will need someway of checking with the Event that this cannot be started, so you can chain up to the root this is why I pass in the event to the Session. If you don't like this way, then just put the methods that manipulate the Session on the Event (so everything is accessed via the Event, which is enforcing all the rules).
In the simplest case, I see the photo as a string (value object) in the Session entity. As a first stab I would do something like this:
// untested, do not know if will compile!
public class Event
{
List<Session> sessions = new List<Session>();
bool isEventClosed = false;
EventId NewSession(string description, string speaker)
{
if(isEventClosed==true)
throw new InvalidOperationException("cannot add session to closed event");
// create a new session, what will you use for identity, string, guid etc
var sessionId = new SessionId(); // in this case autogenerate a guid inside this class
this.sessions.Add(new Session(sessionId, description, speaker));
}
Session GetSession(EventId id)
{
reutrn this.sessions.FirstOrDefault(x => x.id == id);
}
bool CanStartSession(Session session)
{
// TO DO: do a check session is in our array!!
if(this.isEventClosed == true)
return false;
foreach(var session in sessions)
{
if(session.IsStarted()==true)
return false;
}
return true;
}
}
public class Session
{
List<GuestId> guests = new List<GuestId>(); // list of guests
List<string> photoUrls = new List<string>(); // strings to photo urls
readonly SessionId id;
DateTime started = null;
DateTime ended = null;
readonly Event parentEvent;
public Session(Event parent, SessionId id, string description, string speaker)
{
this.id = id;
this.parentEvent = parent;
// store all the other params
}
void AddGuest(GuestId guestId)
{
this.guests.Add(guestId);
}
void RemoveGuest(GuestId guestId)
{
if(this.IsEnded())
throw new InvalidOperationException("cannot remove guest after event has ended");
}
void AddPhoto(string url)
{
if(this.photos.Count>10)
throw new InvalidOperationException("cannot add more than 10 photos");
this.photos.Add(url);
}
void Start()
{
if(this.guests.Count == 0)
throw new InvalidOperationException("cant start session without guests");
if(CanBeStarted())
throw new InvalidOperationException("already started");
if(this.parentEvent.CanStartSession()==false)
throw new InvalidOperationException("another session at our event is already underway or the event is closed");
this.started = DateTime.UtcNow;
}
void End()
{
if(IsEnded()==true)
throw new InvalidOperationException("session already ended");
if(this.photos.length==0)
throw new InvalidOperationException("cant end session without photos");
this.ended = DateTime.UtcNow;
// can raise event here that session has ended, see mediator/event-hander pattern
}
bool CanBeStarted()
{
return (IsStarted()==false && IsEnded()==false);
}
bool IsStarted()
{
return this.started!=null;
}
bool IsEnded()
{
return this.ended!=null;
}
}
No warranty on the above, and may well need to change over time as the understanding evolves and as you see better ways to re-factor the code.
A guest cannot be removed once a session has ended - this logic has been added with a simple test.
Talk about deletion of guests and leaving sessions with 0 guests - you have stated that guests cannot be removed once an event has ended... by allowing that to happen at any point would be in violation of that business rule, so it can't ever happen, ever. Besides, using the term to delete a person in your problem space makes no sense as people cannot be deleted, they existed and will always have a record that they existed. This database term delete belongs in the database, not in this domain model as you have described it.
Is this.parentEvent.CanStartSession()==false safe? No it is not multithread safe, but commands would be ran independently, perhaps in parallel, each in their own thread:
void HandleStartSessionCommand(EventId eventId, SessionId sessionId)
{
// repositories etc, have been provided in constructor
var event = repository.GetById(eventId);
var session = event.GetSession(sessionId);
session.Start();
repository.Save(session);
}
If we were using event sourcing then inside the repository it is writing the stream of changed events in a transaction, and the aggregate root's current version is used so we can detect any changes. So in terms of event sourcing, a change to the Session would indeed be a change to its parent aggregate root, since it doesn't make sense to refer to a Session event in its own right (it will always be a Event event, it cannot exist independently). Obviously the code I have given in my example is not event sourced but could be written as so.
If event sourcing is not used then depending on the transaction implementation, you could wrap the command handler in a transaction as a cross cutting concern:
public TransactionalCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand>
{
private ICommandHandler<TCommand> decoratedHandler;
public TransactionalCommandHandlerDecorator(
ICommandHandler<TCommand> decoratedHandler)
{
this.decoratedHandler = decoratedHandler;
}
public void Handle(TCommand command)
{
using (var scope = new TransactionScope())
{
this.decoratedHandler.Handle(command);
scope.Complete();
}
}
}
In short, we are using the infrastructure implementation to provide concurrency safety.
I can get room's clients list with this code in socket.io 0.9.
io.sockets.clients(roomName)
How can I do this in socket.io 1.0?
Consider this rather more complete answer linked in a comment above on the question: https://stackoverflow.com/a/24425207/1449799
The clients in a room can be found at
io.nsps[yourNamespace].adapter.rooms[roomName]
This is an associative array with keys that are socket ids. In our case, we wanted to know the number of clients in a room, so we did Object.keys(io.nsps[yourNamespace].adapter.rooms[roomName]).length
In case you haven't seen/used namespaces (like this guy[me]), you can learn about them here http://socket.io/docs/rooms-and-namespaces/ (importantly: the default namespace is '/')
Updated (esp. for #Zettam):
checkout this repo to see this working: https://github.com/thegreatmichael/socket-io-clients
Using #ryan_Hdot link, I made a small temporary function in my code, which avoids maintaining a patch. Here it is :
function getClient(roomId) {
var res = [],
room = io.sockets.adapter.rooms[roomId];
if (room) {
for (var id in room) {
res.push(io.sockets.adapter.nsp.connected[id]);
}
}
return res;
}
If using a namespace :
function getClient (ns, id) {
return io.nsps[ns].adapter.rooms[id]
}
Which I use as a temporary fix for io.sockets.clients(roomId) which becomes findClientsSocketByRoomId(roomId).
EDIT :
Most of the time it is worth considering avoiding using this method if possible.
What I do now is that I usually put a client in it's own room (ie. in a room whose name is it's clientID). I found the code more readable that way, and I don't have to rely on this workaround anymore.
Also, I haven't tested this with a Redis adapter.
If you have to, also see this related question if you are using namespaces.
For those of you using namespaces I made a function too that can handle different namespaces. It's quite the same as the answer of nha.
function get_users_by_room(nsp, room) {
var users = []
for (var id in io.of(nsp).adapter.rooms[room]) {
users.push(io.of(nsp).adapter.nsp.connected[id]);
};
return users;
};
As of at least 1.4.5 nha’s method doesn’t work anymore either, and there is still no public api for getting clients in a room. Here is what works for me.
io.sockets.adapter.rooms[roomId] returns an object that has two properties, sockets, and length. The first is another object that has socketId’s for keys, and boolean’s as the values:
Room {
sockets:
{ '/#vQh0q0gVKgtLGIQGAAAB': true,
'/#p9Z7l6UeYwhBQkdoAAAD': true },
length: 2 }
So my code to get clients looks like this:
var sioRoom = io.sockets.adapter.rooms[roomId];
if( sioRoom ) {
Object.keys(sioRoom.sockets).forEach( function(socketId){
console.log("sioRoom client socket Id: " + socketId );
});
}
You can see this github pull request for discussion on the topic, however, it seems as though that functionality has been stripped from the 1.0 pre release candidate for SocketIO.
We are trying to use Altbeacon library to satisfy the next study case:
We want to put several IBeacons in a room or corridor with a distance of no more than 3 meters between each of them, and we want to get the current closest Ibeacon based on the user phone which scans for the beacons.
We first tried to build regions with only one beacon each, wondering that a region were a closed set, meaning that when you enter in a region, you couldn’t be in other region at the same time, and when you leave a region, you enter in the next closest one and so. But that’s not the approach that the library implements.
We want to know if there’s any way in Altbeacon library to apply our approach or if some kind of patch has to be made to satisfy the study case that I present to you.
The easiest way to accomplish this goal is to range for all beacons using a single region, and start ranging:
#Override
public void onBeaconServiceConnect() {
try {
// Set up a region that matches all of your beacons. You may want to replace the first
// null with a UUID that all your beacons share.
Region allBeaconsRegion = new Region("all beacons", null, null, null);
beaconManager.startRangingBeaconsInRegion(mAllBeaconsRegion);
beaconManager.setRangeNotifier(this);
} catch (RemoteException e) {
Log.e(TAG, "Cannot connect to beacon service");
}
}
Note, if you are using a custom Application class with the RegionBootstrap, you can put the above code above inside the didEnterRegion method instead of inside the onBeaconServiceConnect method.
Once you start ranging, you will get a callback once per second with a list of all visible beacons. You can add code to determine which one is closest:
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region arg1) {
Beacon closestBeacon = null;
for (Beacon beacon : beacons) {
if (closestBeacon == null) {
closestBeacon = beacon;
}
else {
if (closestBeacon.getDistance() > beacon.getDistance()) {
closestBeacon = beacon;
}
}
}
// Do Something with closestBeacon here
}
Keep in mind that the closest beacon may change back and forth due to radio noise, so you probably need to add extra logic to protect against the closest beacon flipping back and forth too often.
I need some help with pseudocode. The question is as follows:
Write pseudocode for a function, processPayment() that processes
payment by customers and commits the system to delivering the promised
product and service. This function may call on other functions,
possibly from other objects. You do not have to describe the called
functions or the classes that they belong to as long the calls are
reasonably explanatory.
Advertising is displayed while the customer
awaits credit approval. (i.e., you can assume that while the function
is waiting for credit card approval to complete, the next step begins
immediately.)
Advertising is removed as soon as credit acceptance or
denial is received. You can assume that the user has already entered
credit card information and is aware of the costs of each option.
I have this as pseudocode:
processPayment()
do displayAdContent();
while paymentConfirmation(bool) = false;
I keep thinking I need something after processPayment(). Any guidance would be appreciated!
You need a lot more than "something after processPayment()." I would do something like this:
ProcessPayment()
{
if(paymentIsValid)
{
do displayAdContent();
if(isInInventory())
{
try
{
do createAndChargeOrder();
do deliverProduct();
do updateInventory();
}
catch
{
do cancelOrder();
do sendFailedOrderNotification();
}
}
else
{
do notifyNotAvailable();
do offerSimilarProduct();
}
do sendConfirmation();
}
else
{
do paymentNotValid();
}
}