Would appreciate some architectural advice on some...
# cfml-general
d
Would appreciate some architectural advice on some changes I want to make to an existing API (Taffy). I currently have an endpoint ‘/news/publish/[newsid]’. The resource cfc that processes this endpoint carries out various functions related to the newsid being posted, mainly to create a published version of the news record in the database. In the current version of the cfc it also sends an email to a system editor (distinct from the person publishing the news record). I would like to abstract the section of code that sends the system editor email so that I can handle different requirements for different “partners”. Currently the system editor is one such “partner”. I was hoping to create a cfc with a standard set of public methods for each “partner”. Internally this could then handle their different requirements. For one partner it could send an email for another it could post to an external api etc. Would these “partner” cfcs be beans or factory or something else?
z
well, those are all just components, what you do inside them is up to you
w
in my view, the partner configs would be (dumb) beans that contain only the configuration data for how/where to notify relevant people/processes, but the service level component is the one to actually DO the notifications after having read in the partner bean
you'd create the relevant partner beans and drop them into the service method to read and act upon what's in them
d
Yes, what would be the best way to “call” their public method. Say the main resource cfc loops through an array of “partners” that have been selected by the news publisher. Each partner would have different requirements. So how would I reference each partner’s cfc public “publish” method? ie. createObject()?
w
i wouldn't go about it that way. as i referred to above, beans are dumb, and only know about what's inside them. they don't 'do' anything. you pass those into a service method that reads the bean data/properties and invokes the appropriate service level functions for each type of notification (email, log, whatever) based on the data in the partner config bean
d
So a bean is the correct idea. I think the bean would need to have more than just config as it may carry out various actions for each partner. For one partner it may just be an email with some links. For another it may post to their external api.
So, each partner would have their own service component as well?
w
imo you're putting too much onto the bean. beans are 'data envelopes' in my perspective
no
let me drum up an example of what i'm suggesting, one minute
d
Thanks @websolete
w
bearing in mind that i just wrote this now, it's intended to show the spirit of what i'm trying to convey, ignore code quality:
Copy code
// partner X bean: 
component accessors="true" {

	property name="partnerId" default ="";
	property name="notificationType" default="email"; 
	property name="recipientList" default="";
	property name="recipientCCList" default="";
	property name="logPath" default = "";

}

// notification service
component {

	public void function handleNotifications( required array partnerBeans ) {
		for( var b in arguments.partnerBeans ) {
			notify( b );
		}
	}

	public void function notify( required any partnerBean ) {
	
		// what type of notification?
		var type = partnerBean.getNotificationType(); 

		if( type == "email" ) {
			callEmailFunction( recipients = partnerBean.getRecipientsList(), cc = partnerBean.getRecipientsCCList() );
		}
		else if( type == "log" ) {
			callLogFunction( logPath = partnerBean.getLogPath(), partnerId = partnerBean.getPartnerId() );
		}

		// etc....
		
	}

}
the main point is the bean is just a data container, a fancy struct if you will, and the service takes that ball of data and does something with it based upon the beans contents
i don't like my beans to have any functionality other than to mutate their own data
note that the beans do NOT have to have their values hardcoded, although you can do it. you could also just create a generic partnerBean.cfc and populate it with data from a db before passing it to the notificationService
d
OK, I think I see how that could work. So my resource cfc could grab the partners config data from the database and then create the partner bean and then pass the bean to a generic service cfc that would handle the different requirements. I like that idea.
w
sure. in that scenario the bean stage is certainly optional, you could just go to the db, but i'd still create/populate/use a bean since that will force the notification methods to be generic and non-partner specific
anyway, based on what you describe that's how i would approach it
d
That’s what I’m keen to achieve - each partner can have their own “pathway”.
Thanks for the very useful suggestions.
w
and if it happens frequently you could even persist a 'pool' of these beans in some persistent scope and read them from memory, even lazy load them into the pool
anyway, you get it
e
Why would you make this harder on yourself then you need? copy the current code, then add a pre-processing page, that simply looks for a var, for normal processing, Case #MyVar# do normal, else case #MyOtherVar# query myLookUp and then do something else. You could then fork it per partner by VarID or something else. that way when you can set an "API" and rup app ruleset per "partner"
w
sounds like a maintenance nightmare. you have a new partner, create a new partner bean for them, nothing else changes. if i'm reading what you're saying, have to say i'm not a fan of hardcoding specific behaviors into methods like that
e
Without knowing a bit of the guy's code, skill set, or anything else, the guy asks for help. Your bias is it's a nightmare, my bias is I work with kids who barely can code a html5 blank page. He could literally have a single component that just looks up a list or table and does something else upon request. If that is a "nightmare" then someday I will show you some sample code where the last dev mixed CSSCRIPT with JS Script, which I ended up turning to tag soup as it was less painful to read. He just needs direction, which is CFwheels or the ON REQUEST auto method is nightmarish, what is the simple answer besides you don't like it?
w
he demonstrated his knowledge of beans and factories etc in his original question, so it didn't seem necessary to dumb it down. to answer your last question, i'll just restate that i don't think having functions that switch/case based on which account is being handled is a sound approach. better to have the inbound arguments (the bean in this case) dictate what the function should do, and leave the function unmodified. not sure how else to articulate it, but sure not interested in getting into a pissing contest over it
e
No pissing match here, if we all decompile our code in the end it does the same thing. You like the DaoBeans method, I like the KISS method. You like application mixings, I like code purity, meanwhile, he has an issue. Now he has two different paths to choose from. Both will compile and decompile with nearly identical results. In the end, He wins by our discussion and I get to further sted fast in my belief after work I am hitting the pub. WIN WIN!
w
¯\_(ツ)_/¯
d
Thanks for the suggestions, sorry I missed the fireworks whilst out at rugby. I can see both sides. A lot of the code uses services and data gateways etc to abstract various elements but there is a lot of K.I.S.S. procedural code mixed in there as well when the deadline meant just get it to work and we’ll refactor later. I am a sole developer so don’t have anyone to bounce ideas off so appreciate you taking the time.
I’m actually thinking, in the short term at least, that I will add the config settings into the db layer. Then the service component can have the different possible generic publishing paths (email, api etc) built in that then uses the passed in config (either from bean or db). That way if a new partner comes on board I can just update the database with their config and I don’t need to deploy a new version of the api (it runs on AWS elastic beanstalk) unless there requirements need the service cfc to have another option.
e
We do that at my place for one of our main applications. On App start calls config.cfc which does a simple query to pull everything in an ultra-simple table that has config name, config value, and config description. It runs ultra fast depending on your load and cache parameters.