views:

74

answers:

3

Requirements:

  • multiple sources of input (social media content) into a system
  • multiple destinations of output (social media api's)
  • sources and destinations WILL be added

some pseudo:

IContentProvider contentProvider = context.getBean("contentProvider");
List<Content> toPost = contentProvider.getContent();

for (Content c : toPost) {
    SocialMediaPresence smPresence = socialMediaService.getSMPresenceBySomeId(c.getDestId());
    smPresence.hasTwitter(); smPresence.hasFacebook(); //just to show what this is
    smPresence.postContent(c); //post content could fail for some SM platforms, but shoulnd't be lost forever
}

So now I run out of steam, I need to know what content has been successfully posted, and if it hasn't gone too all platforms, or if another platform were added in the future that content needs to go out for it as well (therefore my content provider will need to not only know if content has gone out, but for what platforms). I'm not looking for code, although sample/pseudo is fine... I'm looking for an approach to this problem that I can implement

A: 

To keep it flexible - for adding new destinations, for instance - instead of incorporating the platform (twitter, facebook) into the function names, keep it more generic.

smPresence.hasPlatform("facebook");

And iterate over all the services it has. Keep posting-success flags in a map, say, indexed by service.

Is that what you're looking for?

Carl Manaster
that is a bad decision.
Bozho
he has two ideas here, which one are you saying is a bad decision? and why?
walnutmon
to have this information (sort-of) hard-coded as string
Bozho
+3  A: 
  1. I don't think it's fine to have the Content know where it is going to be dispatched to. This logic should be external to it.
  2. Adding multiple platforms should be done with something like the Strategy pattern. If you need to add a new one, you'll have to create a new SocialMedia implementation, and modify the service that choses the destinations to add the logic.
  3. you may use dependency injection or singletons instead of using the new operator. (I'm much in favour of DI)

So, something like:

class Content {
  String content; // or whatever type you store it in
  ContentSource source; // who's the source of the content
  // other relevant properties
}


interface SocialMedia {
    boolean post(Content content);
    boolean isContentSuitable(Content content);
}

class Facebook implements SocialMedia {
   boolean post(Content content) {
        // implement posting, return "true" if successful, "false" otherwise
   }

   boolean isContentSuitable(Content content) {
     // decide whether this socialmedia is suitable for this content
     // based on its source, its length or other features
   }
}

class Twitter implements SocialMedia { .. similar to facebook }


class ContentDestinationService {
    List<SocialMedia> getContentDestinations(Content content) {
        List<SocialMedia> result = new ArrayList<SocialMedia>();
        SocialMedia facebook = new Facebook();
        if (facebook.isContentSuitable(content)) {
           result.add(facebook);
        }
        // etc for others
        return result;
    }
}

And then:

for (Content content : toPost) {
    List<SocialMedia> destinations = 
          contentDestinationService.getContentDestinations(content);

    int successfulPosts = 0;
    for (SocialMedia sm : destinations) {
       boolean success = sm.post(content);
       // do something with this result, for example:
       if (success) {
          successfulPosts++;
       }
    }
}
Bozho
+2  A: 

I'd do something like this:

Store the messages (in their "raw" form) in a table or other persistence structure, associated with their author, and having a timestamp (creation date/time for each message).

Create an association author/publication channel.

Create one (or possibly more) queues of "unsent messages". This queue basic structure is:

   | channelId | MessageId | Status | Last Attempt Timestamp

So assuming I am Pamar, and I am subscribing to Twitter, GBuzz and LinkedIn, when I "post" something on your system I get an entry in the main message table, and the new message gets ID = 7686956 Let's suppose that the message was created at 13:05:06 on 20100428

After having created it, 3 records are added in the queue:

   | channelId | MessageId | Status | Last Attempt Timestamp
   | LinkedIn  | 7686956   | New    | 20100428 13:05:06
   | Twitter   | 7686956   | New    | 20100428 13:05:06
   | Gbuzz     | 7686956   | New    | 20100428 13:05:06 

(note that while I wrote "LinkedIn" I expect to have a record Id there and not a string)

Now, you will have one process getting records from this queue (or maybe one or multiple process for every channel, your choice how you want to scale this) accessing the queue, possibly sorted from oldest attempt to newest - this "worker" thread attempts to post on external channel, updates the last attempt timestamp, and sets the status (OK, Failed). Another worker can delete "OK" records in the background.

Now, what happens when you add "Facebook" to my list of channels?

Easy, this operation will have a timestamp, too - the moment you add the Facebook channel to my user. You access the message table and dump all messages created before this timestamp in the queue:

   | channelId | MessageId | Status | Last Attempt Timestamp
   | Facebook  | 7685963   | New    | 20100429 11:12:08
   | Facebook  | 7680064   | New    | 20100429 11:12:08
   | Facebook  | 7697046   | New    | 20100429 11:12:08 

When you "inject" these messages for the new channel you can decide the rules, for example, only messages from the last week, so that the "throttling" is implicit.

Adding a completely new channel will require adding a couple records in the structure, and developing a worker or a strategy class to connect to the new channel and post there using the relevant login profile and the correct API.

p.marino
great, this is a good looking solution for my needs because I don't don't know what they are going to ask me to do later, this allows me to write a script to change things up later if necessary
walnutmon