I'm working on an App that needs to remember events selected by the user from their calendar and I've run into a problem with recurring events.
For non-recurring events I can just store the eventIdentifier and fetch the event from the Event Store when I need it.
But recurring events all share the same eventIdentifier. When I go back to the Event Store to fetch the event (based on the eventIdentifier) I get the very first event in the recurrence chain ... not the Nth recurrence of the event that the user selected.
I can't persist the user selected events by archiving the entire EKEvent object since EventKit doesn't support NSCoding.
I'm considering storing the eventIdentifier and Start & End dates so that I can fetch the correct event from the Event Store ... but that seems pretty kludgy and might make tracking changes the user makes in their calendar between launches of my App tricky.
Any thoughts or suggestions?
The event identifier alone is not enough even for non recurring events. Indeed, it can change when the user moves the event to a different calendar. For recurring events, it may change upon detaching an occurrence or splitting the recurrence.Therefore, it is common practice to search for events using a subset of information (say title, start and due date). You should not rely on event identifiers.
Unfortunately, the framework does not provide us with the raw data of an event, it just provides all of the occurrences of the events in a specified interval. Therefore, there is no such thing (using the framework) as the possibility of retrieving a single recurring event and then expanding its recurrence to get its n-th occurrence: you need to manually post-process the retrieved events in order to find the ones you are interested to.
The problem here is that the APIs provided are not meant for sync purposes. Many developers have complained and still complain about this by filing a bug/feature request using Radar. Until now, Apple answer about is that the APIs fulfill a different purpose, since sync is automatic. However, this is true when syncing through iTunes, but not programmatically.
Related
My application use cqrs and event sourcing. It's already in production.
Now i must add a business rules. My business rules are in my aggregate root UserAggregate.
My commands :
public class CallUserForMarketingPlanCommand
{
public Guid UserId {get;set;}
public DateTime CallDate {get;set;}
public Guid PlanId {get;set;}
}
public class AcceptMarketingPlanCommand
{
public Guid UserId {get;set;}
public Date AswerDate {get;set;}
public Guid PlanId {get;set;}
}
... the same thing for RefuseMarketingPlanCommand
these commands are applied on my aggregate root which generate events stored in event store
Now if 50 days after the call, the user do not give answer, the user must be recalled by operator. To do this, i think generate event UserDoNotRepliedInDelayEvent and use it to project to a read model with recall informations.
My solution is to create a deferred command (from UserCalledForMarketingPlanEvent handler) CheckUserAnswerCommand which check the call date and generate UserDoNotRepliedInDelayEvent if necessary across the aggregate. Ok.
My problem is how to deffered this command on users already in my event store (before this change) ?
EDIT :
Without considering deferred message, how to change business rules (or a business rules parameter) affecting the state of an aggregate. Simple example :
Disable account if two payments are not permformed.
this rule come with the first deployement. Ok, now there are 1000 accounts disabled. The boss change the rule because the business is impacted, and want disable account if 5 payments are not performed.
How to enable account having less than 5 payments not performed ?
Thanks for your help.
Now if 50 days after the call, the user do not give answer, the user must be recalled by operator. To do this, i think generate event UserDoNotRepliedInDelayEvent and use it to project to a read model with recall informations.
If I undestood your question correctly, the main point here, is that the user "not replying" in time is not an action (command) of your domain, quite the contrary, it is the absence of an action. So in this scenario, I don't think you need an event at all.
You simply need a read model which will register all sent invitations and their statuses (whether they're replied, their reply dates and how long did they stand unanswered). Then, you can check this read model for unanswered invitations that exceed your deadline of 50 days (which should be simple enough at this point).
So, up to this point, no new events are generated in your "Invitations" event store. You're simply interpreting the store into a specific read model that will answer you a question you have (which invitations were not answered).
From this point, it depends on your architecture.
You might want a recurring process to check this read model for invitations that exceed your deadline, having those specific invitations trigger a "InvitationExpiredEvent" or something to notify the interested parties (those who will resend them, for instance)
Or you simply might want a more passive approach, not needing an extra event, simply reading this Read Model when appropriate (on the GUI, maybe) and listing the expired invitations.
This will then fix itself... since you can generate the read model retroactively (finding users from any given point in the that never answered their invitations) and put them through the re-invitation pipeline.
Without considering deferred message, how to change business rules (or a business rules parameter) affecting the state of an aggregate. Simple example :
Disable account if two payments are not permformed.
this rule come with the first deployement. Ok, now there are 1000 accounts disabled. The boss change the rule because the business is impacted, and want disable account if 5 payments are not performed.
How to enable account having less than 5 payments not performed ?
This part of your question is more confusing. From what I understood, you once had a rule that stated "Accounts with two or more expired payments should be inactivated" and you want to change this rule to "Accounts with five or more expired payments should be inactivated". If that's the case, you have to deal with this on multiple levels...
First, you must first implement the new rule on your command model, the same way it always have been but with the updated parameter.
Second, you cannot retroactively reactivated accounts with 2,3,4 expired payments by ignoring their "deactivation events". From your event store point of view, this happened and you must abide by the rules that an event store is a "push only" storage. So, you must use compensating events to reactivate them after the rule change.
So, if you took care of the first topic (and your domain is up and running with the new rule) and since you can't take a shortcut because of the second topic, one of your easier options is to simply develop a one-shot operation that will find accounts with 2,3,4 expired payments that are currently disabled and append to their event stores a reactivation event. At this point you will have to regenerate any affected read models if your architecture doesn't do this automatically.
That way, the next time commands are executed against these accounts, their event stores will reflect the fact that they have been reactivated and thus are currently active.
From an event store point of view... each of these accounts will have something like this on their event streams:
... > Payment Expired > Account Disabled > (maybe other stuff happened) > Account Re-Enabled
So your event store will be a pretty accurate representation of your business scenario... once you chose to disable accounts with only 2 expired payments, so a certain account was disabled by that... later you changed your mind, and even without paying their debts, these accounts were re-enabled.
EDIT:
In fact, i think the problem can be summarized by "how to integrate retroactive rules in event sourced system"
If that's the case, than the answer will be more focused on the lines of "there shouldn't be retroactive actions in an event-sourced domain".
As I said in my original answer, an event stream should be a "push-only" storage and that's mainly because only the exact order of events, as they happened, can guarantee the integrity of your rules as they were when those events happened. In that sense, an event storage is less flexible than a traditional one as it will be way more sensitive to external interference and that will sometimes be a pain (were used to meddling with the data sources directly to fix stuff).
However, we should really try to keep the rule and acknowledge that whatever happened, happened and can't be changed. What you can do, is add, to the end of event stream "compensation events" that is, new events that will register a change of state at a given time to reflect your rules-changing. And then you will need a one-shot process to go through your entities and decide which of them are eligible for such a compensating event.
Now, of course, rules are meant to be broken when needed and with enough consideration, you can go wild into the event store. Just know the risks. If you choose to go "full time machine mode" into the event store, the main risks you will face (and should guard against) are:
Entities going into invalid states in their lifetime. It doesn't matter the entity "ends" the event stream in a valid state. You must validate it never enters an invalid state as that is a prerequisite of event streams. So, for each entity affected by your editing, you will need to evaluate its validity step by step through the new event stream.
Mismatches between source code and event stream. This is a little trickier. But one of the maneuvers you can pull with an event sourced system is rollback your source code repository to a given date and "discard" events from that date forward. That way, you can re-execute actions as they would have happened in the past. If you edit past events though, you might face situations where the recorded events don't match what would have happened in the past based on source code. That might be critical and extremely misleading in the future. You should monitor that.
If your architecture integrates different contexts/domains/microservices, that might also need further evaluation. Say context-A issued a cross-boundary message to context-B because of a given state of an entity. Moving forward, you change the entity state by meddling with the event stream. Now there's a chance these contexts might be left inconsistent between them as context-B believes that entity had a state it no longer has. This might be very relevant in your scenario.
you could also use a Saga that keeps track of the process and than create a command like "recallneeded" when the time is up. it also keeps track of events that tells the Saga to complete if there was a call within the 50 days. (Keep in mind that a Saga is part of your Domain logic and acts as an AR if doing DDD)
There's a lot of events happening all the time on my university campus, and, together with a few other students, we thought it would be nice to provide the event schedule as a calendar. So organisers register their event on the intranet, and it gets added to an icalendar file which people on the campus can subscribe to.
This works great when people load the calendar url on their iPhones, but it doesn't when loading in Google Calendar. We have noticed two problems:
When you subscribe to the calendar and then log out and back in, events are no longer visible. Sometimes, clicking refresh fixes it. The vents do not disappear from android devices associated with your account.
When an event is removed from the icalendar file (eg. if it's cancelled), it still remains on the android devices that sync with any google account that subscribed to the calendar. New events sync fine, though, so it's not that the sync didn't happen.
Do you know how I can solve these two problems? I've noticed the STATUS:CANCELLED property in VEVENTs, but it doesn't seem to work when the calendar method is PUBLISH.
Thanks!
PS: If you can suggest a way to test changes faster that waiting for Google to pull the changes from the server, it would be great; right now, I have to wait about 6 hours between each test...
my understanding is that removing it from the file is not the way to cancel an event. One must ensure that there is a UNIQUE identifier to match any changes.
Also must follow the spec for cancelling/changing an event.
See How to cancel an calendar event using ics files?
If all of that is correct, then the various applications that 'subscribe' to a calendar should in theory update the event status when they read the updated file. Unfortunately the speed and frequency of that is up to that application. (NB: note also difference between subscribe and "import")
Yes I have noticed that google is slow to update sometimes. Only thing I can think of is use another application where you have control perhaps over the subscription update frequency to test if the way that you are cancelling an event is working. Once you see the cancellations happening there, then resume testing on google (I have noticed Google is more pedantic than some apps, so you may still have to work to get it 100% working on google.)
Hope that helps!
I've tried the suggestions but Google Calendar only ever adds another event. The iCalendar validators say that the files I generate are valid, and iCal on the Mac removes an event if it has cancel information. But neither Google Calendar or Outlook do. Rather frustrating.
I am currently drafting a concept for a (mostly) HTML-based collaboration suite which I plan to implement using CQRS. This software will contain messages that can be sent to the user (which can either be read or unread, obviously) and other elements which shall be marked "new" if they were created after the last user login.
Hardly something new, but I am not quite sure how that would be correctly implemented using CQRS. As I understand it, Change of any kind should, without exception, only be possible via Commands. But creating commands for every single (new) element that is being accessed seems a bit too much, not to mention the overhead.
I don't know if I need it, but what would be the best way to implement a Last-Accessed Timestamp on elements. Basically the same problem like the above, with the difference that the change happens EVERY time the element is accessed, not only the first time for each user.
CQRS seems to be an awesome concept but it really needs more learning material. Can't wait till a book is released :)
Regards
[Edit] No one? Wouldn't have thought that this is such a complicated issue..
I assume you're using event-sourcing in which case once you allow your query-service/event-handlers to raise appropriate events then this becomes fairly easy to solve.
For your messages/elements; when handling the specific creation events of your elements either add to existing or create additional event-handlers, to store to a messages read-model with a status of new and appropriate information about the element.
As part of you're user login I don't see why you can't raise a user-logged-in event (from the security/query service depending on how your implementing authentication) to say the user has logged in. An event-handler could capture this and write the last-login timestamp to a specific user-last-login read-model.
In addition the user-logged-in event-handler would need to update all the new messages (for that user) to an unread status. Seeing as we're changing the status of the messages as the user logs in do you still need to store the last-login timestamp?
For your last-accessed timestamp, perhaps you could just work this into your query service as queries for your different elements complete. Raise a query-completed event with element id/type information.
The app I'm developing will write events to a calendar on the user's phone. I was thinking of preceeding my app's event with a prefix like myapp: so I can find my events in the event store to display in a tableview.
However, it seems that the only predicate available is based upon a start and end date (– predicateForEventsWithStartDate:endDate:calendars:) when I review the ios eventkit framework. Am I reading that right?
Should I just then create a calendar for just my app on the phone or is there another way to identify my events beside dates?
Thanks in advance.
One other option would be to have local storage (sqlite, coredata etc...) which indexes into the event. Each event has as event id and you can you retrieve the event from the store via eventWithIdentifier.
Not sure what your data access patterns are but that also allows you to quickly store data within your app that you can query effieciently (with a powerful predicates and sql syntax). Your local results could just return eventIds and in the the table view call backs you get the id for that row and retrieve from the events store.
Just another option ...
I'm in the middle of building a simple iPhone calendar app, & I really do need only simple functionality - add event, update event, delete event, with the only complexity being the events need to have the option of being recurring or single. I have managed the add & delete event bit easily enough, but I'm finding it impossible to do the other bits, namely -
add an event with recurrences (ie, up to a certain date, every x days, etc)
edit an existing event, so that it recurrs or stops recurring
edit a specific recurrence of a recurring event / or all recurrences
I think I can manage the other stuff, but the GData documentation seems almost non-existent (if anyone can point me to some meaningful objective-c docs for the GData stuff, I'd be a really happy bunny - at the moment I'm gradually trawling through all its code to try & figure out how to use it)
Any code samples for the above would be very much appreciated!
Many thanks.
add event with recurrences:
Recurrences are expressed in iCalendar format. You have to fortunately know only fields DTSTART, DTEND, EXDATE, RDATE, RRULE. Also notice, that alarms are placed in the root of xml event entry. (You cannot use when and recurrence tag together). Google calendar web app does not mark RDATE, EXDATE, but understands them and other applications can mark them.
edit recurring event:
If you are able to post normal and recurring event it should be no problem to update it. I tested update to google from normal to recurring and back without no problem.
edit a specific recurrence of a recurring event
Exceptions from recurrence are expressed as new events (confirmed or canceled) which have link (in tag) originalEvent to the recurring event. Be aware that modification of a field in a recurring event can automatically change the field in the events exceptions (e.g. title). If you remove the originalEvent while updating to google, google silently ignores it.