#dbs ahoy
Explore tagged Tumblr posts
Text
thirteen entire years ago i commissioned brizy for a pic of the entire zydencrew. it's still one of my favourite commissions of all time, just so wacky and cute. but we relaunched zydengame and redesigned all the characters, and brizy's art has obviously changed and improved loads over that amount of time. so i finally got an updated version! i will still love the original for its style and expression but it is great having my new crew together again.
Firebrand Dakkan (Fire Aspect/godblood of Siakal) Marahil Red (Water Aspect) Lintha Ng Hut Yjrow Soon (Earth Aspect) Ledaal Zyden, the Spear of Heaven (Fire Aspect/Exigent of Himself) Sesus Chenow Thegora (Wood Aspect) Panahon (Air Aspect/godblood of Sikunare)
7 notes
·
View notes
Text
Funny thing about the Jules post, is how easy it is to find him saying it, calling her by it, referencing her by it. And I know there are more episodes featuring it, I just snagged as many as I could because I’m impatient. The only continuity CSI had, was DB Russell calling Finn “Jules” as often as possible in a three year span.
#julie finlay#db russell#and I bet he would have done it more if there had been more time#csi crime scene invesitagtion#continuity ahoy#I spend way too much of my life thinking about them#it’s normal#right#right?!?!#😅
3 notes
·
View notes
Text
Guess who’s really back to Precure Hell? You guessed it, me!
I just finished watching Hugtto Precure (and wow was it a trip to actually watch a precure series from the beginning...I haven’t done that since Mahoupre) and while it could have been better it was entertaining and the animation level definitely was higher than I have seen in a long time.
I might attempt my first precure gifs ever but we will see.
#personal#precure#hugtto precure#i rewatched part of yes precure 5 the last few days so the yes vibes in this WERE TOO REAL#also so much gay going on in this first ep ALONE#also other older precure series references AHOY and i just died#i still need to finish mahoupre and kira kira but that's for another day#now to watch ccs and dbs
8 notes
·
View notes
Text
dragon ball super is about to get pretty hecking gay lads
17 notes
·
View notes
Text
Well... I barely slept. 😂 Least I’ve got coffee... Gonna try to fumble out some words... (Spoilers ahoy!)
I didn’t quite expect the emotional roller coaster this has turned into... and not entirely cause of the episode itself. 😂 It’s feeling ever so bitter sweet... mostly in that experiencing something so emotional without a lot of the people that made up this sorta... fandom home? that it was to me. All the friends I love and care about. It’s... been feeling kinda lonely... Even though I’m not alone of course, but... yeah. It’s hard to explain. 😂 And then with this looking like the end (...though I kinda doubt ITV is gonna shelve the franchise for another 10+ years... least I sure hope not. That’d seem VERY dumb to me. But what will they do with it? That’s the real big question, ain’t it? XD I was a bit agape after attaching ‘Season’ to every time they spoke of ending that today they finally changed their tune and was like THE END. What the ever loving heck, ITV. Seriously. 😂 BUT I digress...) ...IDK if any of that feeling will ever come back now. Shit changes... god, do I ever know it does... but I guess... I just wasn’t ready for it yet. ;A;
...And boy is my body telling me that. 😂 Anxiety is such an asshole.
ANYHOO.
SO.
This ep.
As I mentioned when I gif’d it (opening my gif making stuff was first thing I did XD) that HUG man. That old hug art I did, the original version (the one you see commonly around now is the tweaked one) was one of my first finished (ish) TAG piece (only few rough doodles and half coloured things were before that lol) that I posted June 10th 2015.... so long ago. (My first post that was some sketches was June 2nd XD Yeah, I’ve been around a long while now, blimey. Not quite the VERY very start, but nearly!)
BUT YEAH. I was wanting this hug for a VERY, VERY long time. XDDD Just, back then, didn’t think it’d involve their Dad too. lol
And then, gosh... Scott in this ep... Last ep was def more Scott heavy... to balance he was stepped back a bit, but... it did make sense? Scott just seems quietly basking in the feeling of having his Dad back. Him hanging back from the hug for a moment having that exchange with his Dad feels a bit like a... passing back the torch? Here they are Dad. I took good care of them. :Db Does that make sense??? IDK. I’m so tired you guys. 😂 But it didn’t feel like Scott needed to say anything. It was written all over his face. He must have been so happy... and relieved. It’s pretty overwhelming in all the best way to hear from someone you haven’t in awhile... so... in this case, it must be turned up to like... an 11. ^^a
Man, I don’t think I can go through this in order so, bear with me if I bounce around.
There’s been so much wondering what this version of Jeff would be like all these years... and in such a short time, we definitely got a LOT to go off of. He’s a Dad who definitely doesn’t hesitate to hug his sons. :3 Fears Grandma as much as the boys, lolololol HUGS HIS ENGINEER. I actually really just... LOVED that scene with Brains. That casual, ‘Hi Brains.’ And then just calling him PARTNER and going right in for a hug... and lol Brains’ lil happy sound. Jeff and Brains were certainly friends in TOS, but there was still a kinda distance between them in that Brains is working for Jeff, but that was definitely laid out to be much different in TAG. I’m not surprised by it, cause Brains is def part of the family, but it was lovely they took the time to show it.
ALSO just how he picked up Virgil was dwelling on something... we don’t really get a window into what exactly... but after all those years, he just... picks right up on Virgil’s in some turmoil. (As said in a tag... MAYBE MOM RELATED? He seems to think Virgil’s worried if he’ll really be okay, which is why I wonder... GONNA DEF WRITE SOME FIC THOUGH. XDb) Like. GOSH. I’m sad we aren’t likely to see more of this Jeff, cause with this single ep, he was absolutely sold on me. And I love he was a total presence... but he did sit back quietly and other than dealing with ‘Brains’ and largely just *watched*. Scott was still in charge here.
So as for the end and what role Jeff is gonna play... WELL... I mean. To be fair here at the end, John is probably changing and heading to the elevator... and I mean, we’ve seen Grandma fill in several times for John... he can’t be awake CONSTANTLY, so I’d always felt a bit like maybe they’d gradually gel into a team with just... one more member. And that seems more likely now that we’ve seen he didn’t just... jump to take command. He sat back and let them do their thing.
And now we can see Jeff is def a bit younger... and as kinda already implied, is definitely one to get his hands dirty, so sometimes (once he’s recovered a bunch) maybe he’ll even go out with the boys. That’d be my guess. ^^a I mean, might be a little kinda jostling at first to settle into a new routine... but while he certainly has a strong presence... he’s also did kinda feel... a bit gentle too? He’s not nearly as stern as TOS!Jeff. It felt like a good balance??? IDK. I’m rambling up a storm. XDDD (...and making walls of text... I should... break these up more lol...)
OKAY ALSO...
WAIIIIT wait. WAIT. This looks familiar. V E R Y familiar... Bridge... rockety thing at bottom... explody warning? Two people inside... *squints* DAY OF DISASTER? IS THAT YOU?
I HAVE A FEELING... If so... //CHEF KISS// VERY NICE. A+ XD
OKAY. WHAT ELSE. Uh. Glad they did end up using Fuse’s (or I guess I could say Clarence’s :D) mixed feelings, which was good and gave Grandma a heck of a moment of awesome. They also served to give some action for Kayo, Penelope and Parker which was good else I suppose they’d have been standing around worriedly. lol Which isn’t really suited to their characters. XD YEAH just lotta nice moments, especially with Kayo and Grandma. ANd just. YEAH. GRANDMA MAN. YOU TELL HIM. and also OMG she was a doctor?! THAT sure explains some things. XD And that’s awesome, gosh.
ALSO the number of HUGS in this ep were A+ Getting a moment with Jeff and Grandma was lovely. :D
LIKE REALLY. LOOK HOW HUG HAPPY THIS DAD IS. I AM PLEASED. A+
And just lol... ‘I don’t even have a car yet.’ ‘You have a rocket.’ ‘...oh yeah. That’s true.’ LOLOL SUCH ALAN. He is too cute.
...AND yeah. IDK. I feel like I’m forgetting things. I’ll ramble them into another post if I think of things. I gotta pop out for a bit. 😂
BUT YEAH. Yeah, there’s few things they didn’t get to addressing, but there were hugs and emotions which was what was really the important thing, right? :Da I don’t really feel like nit picking when had all that. XDDD
(It is kinda interesting how in a way the ending reminded me of Legacy’s a bit with a ...Oh we got an emergency. It’s a good note to leave off of... but goes to show when maybe S1 was written, it was largely on the assumption that it could be the only season... but then were able to hint of S2 in an additional scene. But if S2 hadn’t been commissioned, it was sorta ready to be a finale if it had to. In a way S2 was the same, though with bit more hints of S3 were there so... Just kinda interesting. =Oa But also nice that Rob has left it in a way that NEVER SAY NEVER (to quote Rob) is definitely open. ^^)
ANYHOO. That’s all my rambling for now. ^^
#~OOC Post#Thunderbirds Are Go#TAG Spoilers#Spoilers#Episode: The Long Reach (Part 2)#here is my thoughts finally#and by thoughts I mean#lots and lots of rambling#I am SO so tired#I feel like I'm forgetting something I was thinking of...#but I also might be sleepily delusional lol#anyhoo... :3
35 notes
·
View notes
Video
youtube
I've wanted to cover my face in galaxy for a while... I'm now questioning a lot of things. PATREON 💙: http://bit.ly/2goq6fj DAILY LIVE STREAMS: http://bit.ly/2KxXEIx TWITTER: http://www.twitter.com/HeyoDamo BACKGROUND MUSIC: 1. 'Team' by Till 2. 'Raspberry Soda' by Till http://bit.ly/2Dk6XqA 3. 'Extraordinaire' by DJ Quads http://bit.ly/28SxTiN ------------------------------------------------------------------------------ TWITTER: http://www.twitter.com/HeyoDamo FACEBOOK: http://bit.ly/1Ebb6s3 INSTAGRAM: http://bit.ly/1zTznw5 SNAPCHAT: HeyoDamo (barely use it tho) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ THANK YOU SO MUCH TO MY PATRONS FOR SUPPORTING ME! Kibaki Kyarako Sean Francis (Tanukiami) Chazzy Wolf Failed Offerings Alban Nightfury Rojan Easo Snooper Logan murrow Ikideruuu BooshieBoo Casandra Wagner SolomonRivers Dibbo Mika Glitch maddy @MalaikaWolf Sax Andy White SceptiX Odin Wolf BlurpleDutchie Kiva the Birb Toxic Moonlight Cartmanland Jonnie Z SchizoBug Daddi Gabe Pips Ahoy! lichtwolf Koma Viper FurtasticStudios Lachlan Halliwell Hazridge Skylar McElroy Worgon James Hefel AntiBlueFox Ragehound Dalmy NovaCat Nos Hyena DB Husky Yuke Weiß Zed Shirogane Flamingo My Mango ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ by HeyoDamo
5 notes
·
View notes
Text
Der Berater - Neue Allphasen
V-Theorie (Pt. 9b)

VoRsIcHt: bitte die FAQs lesen!
Lange Einleitung (muss nicht zwingend gelesen werden) Konspirationen muss man aufbauen. Der obige Hinweis auf die "FAQs" genügt bei dieser Reihe nicht. Da hilft es auch nicht, dass ich seit 21 Jahren zum sprichwörtlichen Brötchenerwerb Buchhaltung mache, zumeist Nebenbuchhaltungsprozesse, oder als was ich mich gerne bezeichne - angelegt an Pessoas Heteronym Soares ["Das Buch der Unruhe"] -, ich bin ein "kleiner Hilfsbuchhalter". Und als solcher buchte ich in der letzten Arbeitswoche einige Rechnungen eines Chinesischen Unternehmens, welches (nur) 6% Steuer aufschlug. Der Zahn der Zeit ging an mir und der seit August 2017 vereinfachten Form des Mehrwertsteuer-Gesetzes des Landes der aufgehenden Sonne offensichtlich vorbei. Deren neues Modell spielt hier (und heute) keine nennenswerte Rolle, und daher werde ich darauf auch nicht näher eingehen. Dennoch hat es mich ins Grübeln gebracht. Das ist leider - und zum Glück - nicht die ganze Wahrheit, denn wäre es so, gäbe es diesen Eintrag jetzt einfach mal nicht. Es spielen immer so viele, oder zumindest einige, kleine Dinge ausschlaggebende Rollen, die auf mich quasi zufällig zusteuern. Da war zum Beispiel ein YouTube-Video, so ein "unscheinbar kleines", von einem jungen Erwachsenen, dass reichlich viel Aufmerksamkeit in die politische Landschaft streute. Ich glaube, es lief und läuft unter dem Titel "Die Zerstörung der CDU". Der Vlogger erwähnte in (s)einer knappen Stunde Sendezeit irgendetwas von Spiralen, auch von der "aufklaffenden" (Schere) zwischen Reichen und Armen und vielen damit einhergehenden Problemen, die mich nicht so wirklich interessieren, aber interessant waren. So weit, so gut. Zu guter Letzt kam in gleicher Woche auch noch ein Kollege herbei und zeigte mir voll schelmischem Stolz eine Rechnung, auf dem der veraltete deutsche Steuersatz von 16% aufgeführt war. Wir spannten sogleich ein Verschwörungskonstrukt, nämlich dass eventuell diese bestimmte, berechnete Leistungen in Verbindung mit einer anderen Rechnungsposition nur eines hervorbringen könnte - eine unglaubliche Ausnahme, die im Zusammenspiel den verminderte Prozentsatz rechtfertigen würde. Ich gehe jetzt darauf aber auch nicht näher ein und hake dieses Intermezzo als dritten Ausschlaggeber für meinen "Heureka"-Moment ab, der das nachfolgende fiktive Geschichtlein real werden ließ. Und damit ist die Einleitung beendet - die Fiktion beginnt.
Kurzer Prolog (ähnlicher Text wie im "1. Teil", nur stark verkürzt)Fiktionen liegen meist näher an der Wahrheit als Aussagen, die eine Wahrheit vorgaukeln. Meine heutige Mission: Neue Allphasen. Ich bin ein Berater für Staatsmänner und Staatsfrauen. Um die Anonymität zu wahren, nenne ich mich im weiteren Verlauf schlicht "Der Berater", wohingegen ich meine Gesprächsobjekte als Verwalter sehe und daher auch als solche - "Die [Der] Verwalter" - tituliere; kurz: DB und DV. Das Nachfolgende ist eine Zusammenfassung einer Konversation zwischen mir (DB) und einem Verwalter (DV), dem gleichen wie beim letzten Diskurs (- leider). Einleitende Worte braucht es nicht, ebenso wenig wie endende. DB: Mission: Neue Allphasen! DV: Was? DB: Sie mögen doch Geld? DV: Nach unserem letzten Gespräch blieb bei mir der Eindruck hängen, dass Geld Sie nicht im Mindesten interessieren würde. DB: Können Sie nicht mal auf eine einfache Frage, eine einfache Antwort raushauen? DV: Um was geht es denn? DB: Um Geld. Passives Nebeneinkommen sozusagen. Sie mögen das doch sicherlich auch, oder? DV: Was jetzt? Geld oder Nebeneinkünfte, für die ich nichts tun muss? DB: Vermerk an mich selbst: Versuche zukünftig einfachere Fragen zu stellen. DV: Nun legen Sie schon los. DB: Allphasen-Netto-Umsatzsteuer mit Vorsteuerabzug ad acta, mehr sag ich nicht. DV: Wie? DB: Vergessen Sie das. DV: Wieso? Ist das nicht die Grundlage unseres Umsatzsteuermodells? DB: Sie haben wohl klammheimlich Dutzende von Walnüssen gefuttert? DV: Ich muss enttäuschen, ich habe Nüsse nicht in meinen Speiseplan integriert. DB: Schade, Schließfrüchte würden Ihnen sicherlich nicht schaden. DV: Kommen wir bitte zurück zu Ihrer "Mission". DB: Stichwort "Neue Allphasen Ahoi" also. DV: Ich hoffe inständig nicht mit den "Alten", weil das Allphasen-Brutto-Gedöns ja seinerzeit nicht besonders geistreich war. DB: Korrigieren Sie mich, wenn ich falsch liege, aber hat sich das Allphasen-Brutto-Umsatzsteuer-Modell nicht nahezu 50 Jahre gehalten? DV: 49, von 1918-1967. DB: Hatten Sie wirklich keine Walnüsse zum Frühstück? DV: Nein. DB: Gut, dann will ich es mit "Alte Allphasen Ade" abkanzeln. DV: Wenn Sie meinen. DB: Ich meine nicht, ich glaube nicht, ich weiß. DV: In Ordnung. DB: Keine Ordnung. Es ist die Ordnung eines ordentlichen Bewusstseins, guter Mann. DV: Bitte, kommen Sie endlich zum Punkt! DB: "In Ordnung". Kurz und knapp: Sie revolutionieren das Steuermodell. DV: Aha. DB: Wissen Sie, ich hatte da einen Traum. DV: Oje. DB: Ihr Land wird ab sofort zur Heimstätte mit dem höchsten Mehrwertsteuersatz. DV: Wie bitte? Sie wissen aber schon, das Bhutan 50% erhebt? DB: Guter Einwand, das soll unser Ausgangswert werden. DV: Was!? DB: Dieser Wert würde natürlich nicht für alle Menschen gelten. DV: Lassen Sie mich raten: Nur für die Reichen? DB: So ungefähr. DV: Warten Sie mal. Ich falle in Ihrem Modell bestimmt unter die Reichen? DB: Hypothetisch akzeptiert. DV: Das war eine Frage, keine Aussage. DB: Sind Sie ein Schwerverdiener? - Ja! Sind Sie reich...? DV: Also gut, bringen wir es mal ganz runter. Gehen wir vom Komplexen ins Plakative über. Ich gehe in einen Supermarkt und kaufe mir beispielsweise eine Banane, die netto 1€ kostet. Und weil ich reich bin, muss ich 50% Steuer zahlen, also 1,50€. DB: Exakt. An Ihnen ist ein guter Marktschreier verloren gegangen. DV: Papperlapapp. Sagen Sie mir lieber: was zahlt bitte ein Armer? DB: So ein richtig Armer? DV: Ja, bettelarm. DB: Der zahlt gar nichts, denn der kauft keinen Bananen. DV: Bringen Sie mich nicht ... DB: ... auf die Palme? DV: Zur Weißglut! Sagen Sie mir, was ein Armer zahlt, sagen wir ein erwerbsloser Hartzer. DB: 1€. DV: Er zahlt keine einzigen Cent Steuer! DB: Wovon auch? DV: Okay, sagen wir, für ein Beispiel, ein Rentner mit 1.000€ Rente im Monat. DB: Wie wäre es mit 833,34€ pro Monat? DV: Von mir aus 833,34€, warum auch immer? DB: Weil der Ruheständler dann 10.000€ pro Jahr hätte, deswegen, fürs Beispiel selbstverständlich. DV: Na gut, und jetzt? Was zahlt er für die Banane? DB: 1€. DV: Auch keine Steuerlast für den Rentner!? DB: Ja, in Ihrem Beispiel - und weil es mir gerade so gefällt, bleiben wir ruhig in Ihrem betont einprägsamen Bilde. Kommen wir zu den Zahlen. DV: Endlich! So gefallen Sie mir. Legen Sie los! DB: Zur Erinnerung: 10.000€ pro Jahr netto = keine Steuerabgaben. DV: So schwer es mir fällt, aber ich habe es verstanden. Wie geht es weiter? DB: 11.000€ = 1 % Steuer, 12.000€ = 2%, 13.000 = 3, et cetera. Bei 20.000 wären wir bei ... na? DV: 10%. DB: Der Walnussverneiner hat zugeschlagen! Gewinnfrage für eine Gratisnuss: Wann wären wir bei 19%? DV: 29.000€. DB: 29.000€ netto Jahresgehalt. Das sind 2.416,67€ netto monatlich. Sagen Sie mir, entspricht dass dem statistischen Durchschnittsnetto eines Bürgers Ihres Landes? DV: Ich würde sagen es ist deutlich höher. DB: Das würde ich aber auch sagen. DV: Definitiv. Ich las mal was von 1.890€ netto. DB: Mag sein, ich studiere nicht die Bilderbuchpresse. DV: Ihre Sache, aber ... ich muss zugeben: Ihr Modell hat einen gewissen Mehrwert. DB: Sagen Sie bloß! DV: Aber was ist mit den Unternehmern? DB: Firmen beschäftigen Menschen für Geld oder auch nicht, und sie erwirtschaften das Geld in erster Hinsicht nur für sich. Firmen, die Sachen produzieren, an- und/oder verkaufen, sollten gar keine Steuern mehr erheben müssen. DV: Was!? DB: Ganz recht, sie zahlen die festgelegten Steuern anhand ihres Reingewinns bis max. 50% - in Ihrem Fallbeispiel am Ende des Jahres. DV: Ähm, ich glaube Sie haben da einen Denkfehler. Das hieße ja Rechnungen ohne Steuern. DB: Nicht unbedingt. Abstrahieren wir weiter auf der allgemeinverständlichen Ebene. Vielleicht erneut mit einem Beispiel. DV: Um ehrlich zu ein, wäre mir das sehr angenehm. DB: Bleiben wir bei den Bananen. Ein Bananenverkäufer hat 3 Vollzeitmitarbeiter. Sein "Unternehmen" macht damit 116.000€ Nettogewinn im Jahr, wären pro Nase 29.000€. DV: Einen Augenblick. Keiner seiner Angestellten wird 29.000€ pro Jahr an Verdienstzahlungen bekommen. Was wäre er da für ein Kaufmann? Er ist der Chef. Und ein Unternehmer bekommt immer mehr als seine Partner. DB: Die Hackordnung spielt keine Rolle. Es ist völlig gleich, wie viel Geld er sich in seine eigenen Taschen steckt und wie viel er seinen Bediensteten davon abgibt. Es soll uns lediglich um die Ansetzung der Steuerlast seines Betriebs gehen. DV: Sind Sie verrückt? DB: Weiterführende Gedanken stehen mir nicht zu. Oder sagen wir es anders: Mich interessieren diese Art von Fragen nicht. Das ist Ihr Job. DV: Wissen Sie was, mir geht das mit den 50% an Steuern auf eine Banane nicht aus dem Kopf. Das wären 43% mehr. DB: Hören Sie doch auf nur an Bananen oder Walnüsse zu denken. Das gilt für alles und für alle, die 5.000€ oder mehr pro Monat rein netto verdienen. DV: Wie bitte!? DB: 29.000€ = 19%, 39.000 = 29, 49.000 = 39, 59.000 = 49. 60.000€ = 50% = 5.000€/Monat. DV: Das war jetzt nicht notwendig. Eines weiß ich jetzt sicher: Sie sind verrückt! DB: Ich mache mir eben nichts aus Geld. Und vielleicht legen andere dann auch nicht mehr so viel wert drauf. Vielleicht reicht es ihnen, auch Ihnen selbst, wenn sie weniger verdienen. DV: Es wird keine Reichensteuer mehr geben, oder? DB: Jetzt sind Sie verrückt! Diese irreguläre Doppelbesteuerung von Substanziellen war ein genialer Einfall. Das steht heute keineswegs zur Debatte. Sie haben das eine schon schwerlich verstanden und wollen zum nächsten übergehen. Das macht so überhaupt keinen Sinn. DV: Ich denke schon. Was ist mit den anderen Produkten. Wir haben doch aktuell zwei Steuersätze, einen regulären und einen Verminderten. Wenn wir im Beispiel der Banane bleiben, wären wir bei Obst und den verminderten Satz. Was ist, wenn ich einen Bananensaft kaufe? DB: Machen Sie sich das Leben nicht schwer und - vor allem - nicht unnötig kompliziert. DV: Sie meinen allen Ernstes, wir sollten den verminderten Satz abschaffen? DB: Ja natürlich. Das sagte ich Ihnen doch schon durch die sprichwörtliche Blume. Und außerdem: Der gute Friedrich Wilhelm von Brandenburg wäre stolz auf Sie, hat er es doch auch nicht anders geregelt. DV: Mit Verlaub, der Vergleich hinkt gewaltig. DB: Das ist völlig einerlei. 1968 war ein scheiß Jahr. Nehmen Sie Abstand von ihrem verminderten Steuersatz und der altbackenen Annahme damit die Verbraucher zu entlasten. In meinem Modell ist dieser Doppelmoralgedanke unhaltbar, und obendrein ist ein zweiter Steuersatz in der Praxis schwer umzusetzen. DV: Guter Einwand. Wie soll das praktisch gehen. Woher soll der Verkäufer im Supermarkt wissen, welcher Steuersatz für mich zu Grunde gelegt wurde? DB: Das ist doch ganz einfach. Sie haben eine Steuerkarte, die einfach nach der letzten Banane, der letzten Walnuss, abgescannt wird. DV: Und was ist, wenn ich mal die Karte nicht bei mir habe? DB: Schön, dass Sie fragen. Hier sind wir auch schon bei meinem Traum. DV: Oje... DB: Ich sehe eine Verkäuferin in den besten Jahren an der Kasse sitzen. Und sie macht das, was sie immer macht, wenn ein Kunde kommt, der sich nicht steuerlich "ausweisen" kann. Sie springt von ihrem Stuhl auf und brüllt aus Leibeskräften: "Maximalsteuersatz! Maximalsteuersatz! 50 Prozent!" - und dabei klingelt sie verzückt an einer Glocke, damit es ja auch alle im Laden hören. Und jeder, wirklich jeder, freut sich mit ihr. DV: Hatte ich schon erwähnt, dass ich Sie für verrückt halte? DB: Einmal als Frage, einmal als Aussage. DV: Oh Mann. Denken Sie überhaupt beim Träumen? Was, wenn diese Person ein Tourist wäre? DB: Solche Annahmen überlasse ich getrost Ihnen, und nur Ihnen und Ihresgleichen. DV: Sie bringen mich um den Verstand! Selbst wenn ich auf Ihren Vorschlag eingehen würde... DB: ...Sie werden darauf eingehen. Sie haben doch keine andere Wahl. DV: Wissen Sie eigentlich, wie viel Arbeit mir da bevorsteht? Diese, für Sie, "unnötigen Detailfragen" werden viele Menschen für viele Stunden zum Kopfzerbrechen bringen DB: Tatsächlich bin ich mir diesen Umständen sehr wohl bewusst. Und auch dazu habe ich eine Vision. DV: Oje. DB: Ich sehe Sie mit vielen Menschen in einem Raum ohne Fenster an der Stirnseite eines langen Tisches sitzen. Ihre gut aussehende Sekretärin kommt in den Raum. Sie trägt Lederstiefel, Minirock und eine weiße Bluse mit großzügigem Ausschnitt. In ihren zarten Händen hält sie eine große Schüssel voller Walnüsse. DV: Gibt es bei Ihnen auch Träume, wo keine Frauen vorkommen? DB: Ich darf doch bitten! Wo denken Sie hin? DV: Wo Sie hindenken, würde ich gerne nicht wissen wollen. DB: Schauen Sie mit mir auf die nächste Szene: Eine stämmige Frau schenkt aus einem riesigen Topf Erbsensuppe aus. Es ist Mittagspause. DV: Und was gibt es zum Nachtisch? DB: Bananeneis. DV: Ich hasse Sie! DB: Hassen können Sie doch nur, weil es Liebe gibt. DV: Ich frage mich wirklich, wann Sie mir mal bei einer Ausarbeitung helfen? DB: Ich bin ein Berater, ich bin kein Lakai Ihres Kabinetts. DV: Dann beraten Sie mich! DB: Darf ich Sie erneut an die "Order of Things" erinnern? Wir setzen Impulse... DV: ... und wir arbeiten sie aus. DB: Oder eben auch nicht, das liegt bei Ihnen. DV: Wir haben doch keine andere Wahl. DB: Können wir das heute mal abkürzen? DV: Ich hasse Sie und Ihresgleichen! DB: Möge die Wand hinter mir Ihr erster Ansprechpartner sein. Mein lieber guter Mann: Glück auf! DV: Glück auf... DB: Sie sind ein guter Mann. Ich glaube das nicht so einfach, ich weiß es. Sie machen Ihr Zeugs. DV: Mein "Zeugs"... DB: So sieht es aus, und so wird es am Ende auch gut ausschauen. Ich entschuldige Sie gerne, wenn Sie Schulden hätten. Haben Sie aber nicht. So entschuldige ich mich bei mir selbst, denn ich weiß, Sie haben jetzt viel zu tun. DV: Ergebensten Dank. DB: Immer wieder gerne. Epilog (...) Wie im Prolog erwähnt, ist jedes weitere Wort verschwendet. VSt ahoi - neue Allphasen juchhei! - Ende der Fiktion -
0 notes
Text
Introducing The Component-Based API
Introducing The Component-Based API
Leonardo Losoviz
2019-01-21T13:00:21+01:002019-01-21T12:14:45+00:00
An API is the communication channel for an application to load data from the server. In the world of APIs, REST has been the more established methodology, but has lately been overshadowed by GraphQL, which offers important advantages over REST. Whereas REST requires multiple HTTP requests to fetch a set of data to render a component, GraphQL can query and retrieve such data in a single request, and the response will be exactly what is required, without over or under-fetching data as typically happens in REST.
In this article, I will describe another way of fetching data which I have designed and called “PoP” (and open sourced here), which expands on the idea of fetching data for several entities in a single request introduced by GraphQL and takes it a step further, i.e. while REST fetches the data for one resource, and GraphQL fetches the data for all resources in one component, the component-based API can fetch the data for all resources from all components in one page.
Using a component-based API makes most sense when the website is itself built using components, i.e. when the webpage is iteratively composed of components wrapping other components until, at the very top, we obtain a single component that represents the page. For instance, the webpage shown in the image below is built with components, which are outlined with squares:
The page is a component wrapping components wrapping components, as shown by the squares. (Large preview)
A component-based API is able to make a single request to the server by requesting the data for all of the resources in each component (as well as for all of the components in the page) which is accomplished by keeping the relationships among components in the API structure itself.
Among others, this structure offers the following several benefits:
A page with many components will trigger only one request instead of many;
Data shared across components can be fetched only once from the DB and printed only once in the response;
It can greatly reduce — even completely remove — the need for a data store.
We will explore these in details throughout the article, but first, let’s explore what components actually are and how we can build a site based on such components, and finally, explore how a component-based API works.
Recommended reading: A GraphQL Primer: Why We Need A New Kind Of API
Ahoy! The hunt for shiny front-end & UX treasures has begun! Meet SmashingConf San Francisco 2019 🇺🇸 — a friendly conference on performance, refactoring, interface design patterns, animation and all the CSS/JS malarkey. Brad Frost, Sara Soueidan, Miriam Suzanne, Chris Coyier and many others. April 16–17. You can easily convince your boss, you know.
Check the speakers ↬
Building A Site Through Components
A component is simply a set of pieces of HTML, JavaScript and CSS code put all together to create an autonomous entity. This can then wrap other components to create more complex structures, and be itself wrapped by other components, too. A component has a purpose, which can range from something very basic (such as a link or a button) to something very elaborate (such as a carousel or a drag-and-drop image uploader). Components are most useful when they are generic and enable customization through injected properties (or “props”), so that they can serve a wide array of use cases. In the utmost case, the site itself becomes a component.
The term “component” is often used to refer both to functionality and design. For instance, concerning functionality, JavaScript frameworks such as React or Vue allow to create client-side components, which are able to self-render (for instance, after the API fetches their required data), and use props to set configuration values on their wrapped components, enabling code reusability. Concerning design, Bootstrap has standardized how websites look and feel through its front-end component library, and it has become a healthy trend for teams to create design systems to maintain their websites, which allows the different team members (designers and developers, but also marketers and salesmen) to speak a unified language and express a consistent identity.
Componentizing a site then is a very sensible way to make the website become more maintainable. Sites using JavaScript frameworks such as React and Vue are already component-based (at least on the client-side). Using a component library like Bootstrap doesn’t necessarily make the site be component-based (it could be a big blob of HTML), however, it incorporates the concept of reusable elements for the user interface.
If the site is a big blob of HTML, for us to componentize it we must break the layout into a series of recurring patterns, for which we must identify and catalogue sections on the page based on their similarity of functionality and styles, and break these sections down into layers, as granular as possible, attempting to have each layer be focused on a single goal or action, and also trying to match common layers across different sections.
Note: Brad Frost’s “Atomic Design” is a great methodology for identifying these common patterns and building a reusable design system.

Brad Frost identifies five distinct levels in atomic design for creating design systems. (Large preview)
Hence, building a site through components is akin to playing with LEGO. Each component is either an atomic functionality, a composition of other components, or a combination of the two.
As shown below, a basic component (an avatar) is iteratively composed by other components until obtaining the webpage at the top:
Sequence of components produced, from an avatar all the way up to the webpage. (Large preview)
The Component-Based API Specification
For the component-based API I have designed, a component is called a “module”, so from now on the terms “component” and “module” are used interchangeably.
The relationship of all modules wrapping each other, from the top-most module all the way down to the last level, is called the “component hierarchy”. This relationship can be expressed through an associative array (an array of key => property) on the server-side, in which each module states its name as the key attribute and its inner modules under the property modules. The API then simply encodes this array as a JSON object for consumption:
// Component hierarchy on server-side, e.g. through PHP: [ "top-module" => [ "modules" => [ "module-level1" => [ "modules" => [ "module-level11" => [ "modules" => [...] ], "module-level12" => [ "modules" => [ "module-level121" => [ "modules" => [...] ] ] ] ] ], "module-level2" => [ "modules" => [ "module-level21" => [ "modules" => [...] ] ] ] ] ] ] // Component hierarchy encoded as JSON: { "top-module": { modules: { "module-level1": { modules: { "module-level11": { ... }, "module-level12": { modules: { "module-level121": { ... } } } } }, "module-level2": { modules: { "module-level21": { ... } } } } } }
The relationship among modules is defined on a strictly top-down fashion: a module wraps other modules and knows who they are, but it doesn’t know — and doesn’t care — which modules are wrapping him.
For instance, in the JSON code above, module module-level1 knows it wraps modules module-level11 and module-level12, and, transitively, it also knows it wraps module-level121; but module module-level11 doesn’t care who is wrapping it, consequently is unaware of module-level1.
Having the component-based structure, we can now add the actual information required by each module, which is categorized into either settings (such as configuration values and other properties) and data (such as the IDs of the queried database objects and other properties), and placed accordingly under entries modulesettings and moduledata:
{ modulesettings: { "top-module": { configuration: {...}, ..., modules: { "module-level1": { configuration: {...}, ..., modules: { "module-level11": { repeat... }, "module-level12": { configuration: {...}, ..., modules: { "module-level121": { repeat... } } } } }, "module-level2": { configuration: {...}, ..., modules: { "module-level21": { repeat... } } } } } }, moduledata: { "top-module": { dbobjectids: [...], ..., modules: { "module-level1": { dbobjectids: [...], ..., modules: { "module-level11": { repeat... }, "module-level12": { dbobjectids: [...], ..., modules: { "module-level121": { repeat... } } } } }, "module-level2": { dbobjectids: [...], ..., modules: { "module-level21": { repeat... } } } } } } }
Following, the API will add the database object data. This information is not placed under each module, but under a shared section called databases, to avoid duplicating information when two or more different modules fetch the same objects from the database.
In addition, the API represents the database object data in a relational manner, to avoid duplicating information when two or more different database objects are related to a common object (such as two posts having the same author). In other words, database object data is normalized.
Recommended reading: Building A Serverless Contact Form For Your Static Site
The structure is a dictionary, organized under each object type first and object ID second, from which we can obtain the object properties:
{ databases: { primary: { dbobject_type: { dbobject_id: { property: ..., ... }, ... }, ... } } }
This JSON object is already the response from the component-based API. Its format is a specification all by itself: As long as the server returns the JSON response in its required format, the client can consume the API independently of how it is implemented. Hence, the API can be implemented on any language (which is one of the beauties of GraphQL: being a specification and not an actual implementation has enabled it to become available in a myriad of languages.)
Note: In an upcoming article, I will describe my implementation of the component-based API in PHP (which is the one available in the repo).
API response example
For instance, the API response below contains a component hierarchy with two modules, page => post-feed, where module post-feed fetches blog posts. Please notice the following:
Each module knows which are its queried objects from property dbobjectids (IDs 4 and 9 for the blog posts)
Each module knows the object type for its queried objects from property dbkeys (each post’s data is found under posts, and the post’s author data, corresponding to the author with the ID given under the post’s property author, is found under users)
Because the database object data is relational, property author contains the ID to the author object instead of printing the author data directly.
{ moduledata: { "page": { modules: { "post-feed": { dbobjectids: [4, 9] } } } }, modulesettings: { "page": { modules: { "post-feed": { dbkeys: { id: "posts", author: "users" } } } } }, databases: { primary: { posts: { 4: { title: "Hello World!", author: 7 }, 9: { title: "Everything fine?", author: 7 } }, users: { 7: { name: "Leo" } } } } }
Differences Fetching Data From Resource-Based, Schema-Based And Component-Based APIs
Let’s see how a component-based API such as PoP compares, when fetching data, to a resource-based API such as REST, and to a schema-based API such as GraphQL.
Let’s say IMDB has a page with two components which need to fetch data: “Featured director” (showing a description of George Lucas and a list of his films) and “Films recommended for you” (showing films such as Star Wars: Episode I — The Phantom Menace and The Terminator). It could look like this:

Components 'Featured director' and 'Films recommended for you' for the next-generation IMDB site. (Large preview)
Let’s see how many requests are needed to fetch the data through each API method. For this example, the “Featured director” component brings one result (“George Lucas”), from which it retrieves two films (Star Wars: Episode I — The Phantom Menace and Star Wars: Episode II — Attack of the Clones), and for each film two actors (“Ewan McGregor” and “Natalie Portman” for the first film, and “Natalie Portman” and “Hayden Christensen” for the second film). The component “Films recommended for you” brings two results (Star Wars: Episode I — The Phantom Menace and The Terminator), and then fetches their directors (“George Lucas” and “James Cameron” respectively).
Using REST to render component featured-director, we may need the following 7 requests (this number can vary depending on how much data is provided by each endpoint, i.e. how much over-fetching has been implemented):
GET - /featured-director GET - /directors/george-lucas GET - /films/the-phantom-menace GET - /films/attack-of-the-clones GET - /actors/ewan-mcgregor GET - /actors/natalie-portman GET - /actors/hayden-christensen
GraphQL allows, through strongly typed schemas, to fetch all the required data in one single request per component. The query to fetch data through GraphQL for the component featuredDirector looks like this (after we have implemented the corresponding schema):
query { featuredDirector { name country avatar films { title thumbnail actors { name avatar } } } }
And it produces the following response:
{ data: { featuredDirector: { name: "George Lucas", country: "USA", avatar: "...", films: [ { title: "Star Wars: Episode I - The Phantom Menace", thumbnail: "...", actors: [ { name: "Ewan McGregor", avatar: "...", }, { name: "Natalie Portman", avatar: "...", } ] }, { title: "Star Wars: Episode II - Attack of the Clones", thumbnail: "...", actors: [ { name: "Natalie Portman", avatar: "...", }, { name: "Hayden Christensen", avatar: "...", } ] } ] } } }
And querying for component “Films recommended for you” produces the following response:
{ data: { films: [ { title: "Star Wars: Episode I - The Phantom Menace", thumbnail: "...", director: { name: "George Lucas", avatar: "...", } }, { title: "The Terminator", thumbnail: "...", director: { name: "James Cameron", avatar: "...", } } ] } }
PoP will issue only one request to fetch all the data for all components in the page, and normalize the results. The endpoint to be called is simply the same as the URL for which we need to get the data, just adding an additional parameter output=json to indicate to bring the data in JSON format instead of printing it as HTML:
GET - /url-of-the-page/?output=json
Assuming that the module structure has a top module named page containing modules featured-director and films-recommended-for-you, and these also have submodules, like this:
"page" modules "featured-director" modules "director-films" modules "film-actors" "films-recommended-for-you" modules "film-director"
The single returned JSON response will look like this:
{ modulesettings: { "page": { modules: { "featured-director": { dbkeys: { id: "people", }, modules: { "director-films": { dbkeys: { films: "films" }, modules: { "film-actors": { dbkeys: { actors: "people" }, } } } } }, "films-recommended-for-you": { dbkeys: { id: "films", }, modules: { "film-director": { dbkeys: { director: "people" }, } } } } } }, moduledata: { "page": { modules: { "featured-director": { dbobjectids: [1] }, "films-recommended-for-you": { dbobjectids: [1, 3] } } } }, databases: { primary: { people { 1: { name: "George Lucas", country: "USA", avatar: "..." films: [1, 2] }, 2: { name: "Ewan McGregor", avatar: "..." }, 3: { name: "Natalie Portman", avatar: "..." }, 4: { name: "Hayden Christensen", avatar: "..." }, 5: { name: "James Cameron", avatar: "..." }, }, films: { 1: { title: "Star Wars: Episode I - The Phantom Menace", actors: [2, 3], director: 1, thumbnail: "..." }, 2: { title: "Star Wars: Episode II - Attack of the Clones", actors: [3, 4], thumbnail: "..." }, 3: { title: "The Terminator", director: 5, thumbnail: "..." }, } } } }
Let’s analyze how these three methods compare to each other, in terms of speed and the amount of data retrieved.
Speed
Through REST, having to fetch 7 requests just to render one component can be very slow, mostly on mobile and shaky data connections. Hence, the jump from REST to GraphQL represents a great deal for speed, because we are able to render a component with only one request.
PoP, because it can fetch all data for many components in one request, will be faster for rendering many components at once; however, most likely there is no need for this. Having components be rendered in order (as they appear in the page), is already a good practice, and for those components which appear under the fold there is certainly no rush to render them. Hence, both the schema-based and component-based APIs are already pretty good and clearly superior to a resource-based API.
Amount of Data
On each request, data in the GraphQL response may be duplicated: actress “Natalie Portman” is fetched twice in the response from the first component, and when considering the joint output for the two components, we can also find shared data, such as film Star Wars: Episode I — The Phantom Menace.
PoP, on the other hand, normalizes the database data and prints it only once, however, it carries the overhead of printing the module structure. Hence, depending on the particular request having duplicated data or not, either the schema-based API or the component-based API will have a smaller size.
In conclusion, a schema-based API such as GraphQL and a component-based API such as PoP are similarly good concerning performance, and superior to a resource-based API such as REST.
Recommended reading: Understanding And Using REST APIs
Particular Properties Of A Component-Based API
If a component-based API is not necessarily better in terms of performance than a schema-based API, you may be wondering, then what am I trying to achieve with this article?
In this section, I will attempt to convince you that such an API has incredible potential, providing several features which are very desirable, making it a serious contender in the world of APIs. I describe and demonstrate each of its unique great features below.
The Data To Be Retrieved From The Database Can Be Inferred From The Component Hierarchy
When a module displays a property from a DB object, the module may not know, or care, what object it is; all it cares about is defining what properties from the loaded object are required.
For instance, consider the image below. A module loads an object from the database (in this case, a single post), and then its descendant modules will show certain properties from the object, such as title and content:

While some modules load the database object, others load properties. (Large preview)
Hence, along the component hierarchy, the “dataloading” modules will be in charge of loading the queried objects (the module loading the single post, in this case), and its descendant modules will define what properties from the DB object are required (title and content, in this case).
Fetching all the required properties for the DB object can be done automatically by traversing the component hierarchy: starting from the dataloading module, we iterate all its descendant modules all the way down until reaching a new dataloading module, or until the end of the tree; at each level we obtain all required properties, and then merge all properties together and query them from the database, all of them only once.
In the structure below, module single-post fetches the results from the DB (the post with ID 37), and submodules post-title and post-content define properties to be loaded for the queried DB object (title and content respectively); submodules post-layout and fetch-next-post-button do not require any data fields.
"single-post" => Load objects with object type "post" and ID 37 modules "post-layout" modules "post-title" => Load property "title" "post-content" => Load property "content" "fetch-next-post-button"
The query to be executed is calculated automatically from the component hierarchy and their required data fields, containing all the properties needed by all the modules and their submodules:
SELECT title, content FROM posts WHERE id = 37
By fetching the properties to retrieve directly from the modules, the query will be automatically updated whenever the component hierarchy changes. If, for instance, we then add submodule post-thumbnail, which requires data field thumbnail:
"single-post" => Load objects with object type "post" and ID 37 modules "post-layout" modules "post-title" => Load property "title" "post-content" => Load property "content" "post-thumbnail" => Load property "thumbnail" "fetch-next-post-button"
Then the query is automatically updated to fetch the additional property:
SELECT title, content, thumbnail FROM posts WHERE id = 37
Because we have established the database object data to be retrieved in a relational manner, we can also apply this strategy among the relationships between database objects themselves.
Consider the image below: Starting from the object type post and moving down the component hierarchy, we will need to shift the DB object type to user and comment, corresponding to the post’s author and each of the post’s comments respectively, and then, for each comment, it must change the object type once again to user corresponding to the comment’s author.
Moving from a database object to a relational object (possibly changing the object type, as in post => author going from post to user, or not, as in author => followers going from user to user) is what I call “switching domains”.

Changing the DB object from one domain to another. (Large preview)
After switching to a new domain, from that level at the component hierarchy downwards, all required properties will be subjected to the new domain:
name is fetched from the user object (representing the post’s author),
content is fetched from the comment object (representing each of the post’s comments),
name is fetched from the user object (representing the author of each comment).
Traversing the component hierarchy, the API knows when it is switching to a new domain and, appropriately, update the query to fetch the relational object.
For example, if we need to show data from the post’s author, stacking submodule post-author will change the domain at that level from post to the corresponding user, and from this level downwards the DB object loaded into the context passed to the module is the user. Then, submodules user-name and user-avatar under post-author will load properties name and avatar under the user object:
"single-post" => Load objects with object type "post" and ID 37 modules "post-layout" modules "post-title" => Load property "title" "post-content" => Load property "content" "post-author" => Switch domain from "post" to "user", based on property "author" modules "user-layout" modules "user-name" => Load property "name" "user-avatar" => Load property "avatar" "fetch-next-post-button"
Resulting in the following query:
SELECT p.title, p.content, p.author, u.name, u.avatar FROM posts p INNER JOIN users u WHERE p.id = 37 AND p.author = u.id
In summary, by configuring each module appropriately, there is no need to write the query to fetch data for a component-based API. The query is automatically produced from the structure of the component hierarchy itself, obtaining what objects must be loaded by the dataloading modules, the fields to retrieve for each loaded object defined at each descendant module, and the domain switching defined at each descendant module.
Adding, removing, replacing or altering any module will automatically update the query. After executing the query, the retrieved data will be exactly what is required — nothing more or less.
Observing Data And Calculating Additional Properties
Starting from the dataloading module down the component hierarchy, any module can observe the returned results and calculate extra data items based on them, or feedback values, which are placed under entry moduledata.
For instance, module fetch-next-post-button can add a property indicating if there are more results to fetch or not (based on this feedback value, if there aren’t more results, the button will be disabled or hidden):
{ moduledata: { "page": { modules: { "single-post": { modules: { "fetch-next-post-button": { feedback: { hasMoreResults: true } } } } } } } }
Implicit Knowledge Of Required Data Decreases Complexity And Makes The Concept Of An “Endpoint” Become Obsolete
As shown above, the component-based API can fetch exactly the required data, because it has the model of all components on the server and what data fields are required by each component. Then, it can make the knowledge of the required data fields implicit.
The advantage is that defining what data is required by the component can be updated just on the server-side, without having to redeploy JavaScript files, and the client can be made dumb, just asking the server to provide whatever data it is that it needs, thus decreasing the complexity of the client-side application.
In addition, calling the API to retrieve the data for all components for a specific URL can be carried out simply by querying that URL plus adding the extra parameter output=json to indicate returning API data instead of printing the page. Hence, the URL becomes its own endpoint or, considered in a different way, the concept of an “endpoint” becomes obsolete.
Requests to fetch resources with different APIs. (Large preview)
Retrieving Subsets Of Data: Data Can Be Fetched For Specific Modules, Found At Any Level Of The Component Hierarchy
What happens if we don’t need to fetch the data for all modules in a page, but simply the data for a specific module starting at any level of the component hierarchy? For instance, if a module implements an infinite-scroll, when scrolling down we must fetch only new data for this module, and not for the other modules on the page.
This can be accomplished by filtering the branches of the component hierarchy that will be included in the response, to include properties only starting from the specified module and ignore everything above this level. In my implementation (which I will describe in an upcoming article), the filtering is enabled by adding parameter modulefilter=modulepaths to the URL, and the selected module (or modules) is indicated through a modulepaths[] parameter, where a “module path” is the list of modules starting from the top-most module to the specific module (e.g. module1 => module2 => module3 has module path [module1, module2, module3] and is passed as a URL parameter as module1.module2.module3).
For instance, in the component hierarchy below every module has an entry dbobjectids:
"module1" dbobjectids: [...] modules "module2" dbobjectids: [...] modules "module3" dbobjectids: [...] "module4" dbobjectids: [...] "module5" dbobjectids: [...] modules "module6" dbobjectids: [...]
Then requesting the webpage URL adding parameters modulefilter=modulepaths and modulepaths[]=module1.module2.module5 will produce the following response:
"module1" modules "module2" modules "module5" dbobjectids: [...] modules "module6" dbobjectids: [...]
In essence, the API starts loading data starting from module1 => module2 => module5. That’s why module6, which comes under module5, also brings its data while module3 and module4 do not.
In addition, we can create custom module filters to include a pre-arranged set of modules. For instance, calling a page with modulefilter=userstate can print only those modules which require user state for rendering them in the client, such as modules module3 and module6:
"module1" modules "module2" modules "module3" dbobjectids: [...] "module5" modules "module6" dbobjectids: [...]
The information of which are the starting modules comes under section requestmeta, under entry filteredmodules, as an array of module paths:
requestmeta: { filteredmodules: [ ["module1", "module2", "module3"], ["module1", "module2", "module5", "module6"] ] }
This feature allows to implement an uncomplicated Single-Page Application, in which the frame of the site is loaded on the initial request:
"page" modules "navigation-top" dbobjectids: [...] "navigation-side" dbobjectids: [...] "page-content" dbobjectids: [...]
But, from them on, we can append parameter modulefilter=page to all requested URLs, filtering out the frame and bringing only the page content:
"page" modules "navigation-top" "navigation-side" "page-content" dbobjectids: [...]
Similar to module filters userstate and page described above, we can implement any custom module filter and create rich user experiences.
The Module Is Its Own API
As shown above, we can filter the API response to retrieve data starting from any module. As a consequence, every module can interact with itself from client to server just by adding its module path to the webpage URL in which it has been included.
I hope you will excuse my over-excitement, but I truly can’t emphasize enough how wonderful this feature is. When creating a component, we don’t need to create an API to go alongside with it to retrieve data (REST, GraphQL, or anything at all), because the component is already able to talk to itself in the server and load its own data — it is completely autonomous and self-serving.
Each dataloading module exports the URL to interact with it under entry dataloadsource from under section datasetmodulemeta:
{ datasetmodulemeta: { "module1": { modules: { "module2": { modules: { "module5": { meta: { dataloadsource: "https://page-url/?modulefilter=modulepaths&modulepaths[]=module1.module2.module5" }, modules: { "module6": { meta: { dataloadsource: "https://page-url/?modulefilter=modulepaths&modulepaths[]=module1.module2.module5.module6" } } } } } } } } } }
Fetching Data Is Decoupled Across Modules And DRY
To make my point that fetching data in a component-based API is highly decoupled and DRY (Don’t Repeat Yourself), I will first need to show how in a schema-based API such as GraphQL it is less decoupled and not DRY.
In GraphQL, the query to fetch data must indicate the data fields for the component, which may include subcomponents, and these may also include subcomponents, and so on. Then, the topmost component needs to know what data is required by every one of its subcomponents too, as to fetch that data.
For instance, rendering the <FeaturedDirector> component might require the following subcomponents:
Render <FeaturedDirector>: <div> Country: {country} {foreach films as film} <Film film={film} /> {/foreach} </div> Render <Film>: <div> Title: {title} Pic: {thumbnail} {foreach actors as actor} <Actor actor={actor} /> {/foreach} </div> Render <Actor>: <div> Name: {name} Photo: {avatar} </div>
In this scenario, the GraphQL query is implemented at the <FeaturedDirector> level. Then, if subcomponent <Film> is updated, requesting the title through property filmTitle instead of title, the query from the <FeaturedDirector> component will need to be updated, too, to mirror this new information (GraphQL has a versioning mechanism which can deal with this problem, but sooner or later we should still update the information). This produces maintenance complexity, which could be difficult to handle when the inner components often change or are produced by third-party developers. Hence, components are not thoroughly decoupled from each other.
Similarly, we may want to render directly the <Film> component for some specific film, for which then we must also implement a GraphQL query at this level, to fetch the data for the film and its actors, which adds redundant code: portions of the same query will live at different levels of the component structure. So GraphQL is not DRY.
Because a component-based API already knows how its components wrap each other in its own structure, then these problems are completely avoided. For one, the client is able to simply request the required data it needs, whichever this data is; if a subcomponent data field changes, the overall model already knows and adapts immediately, without having to modify the query for the parent component in the client. Therefore, the modules are highly decoupled from each other.
For another, we can fetch data starting from any module path, and it will always return the exact required data starting from that level; there are no duplicated queries whatsoever, or even queries to start with. Hence, a component-based API is fully DRY. (This is another feature that really excites me and makes me get wet.)
(Yes, pun fully intended. Sorry about that.)
Retrieving Configuration Values In Addition To Database Data
Let’s revisit the example of the featured-director component for the IMDB site described above, which was created — you guessed it! — with Bootstrap. Instead of hardcoding the Bootstrap classnames or other properties such as the title’s HTML tag or the avatar max width inside of JavaScript files (whether they are fixed inside the component, or set through props by parent components), each module can set these as configuration values through the API, so that then these can be directly updated on the server and without the need to redeploy JavaScript files. Similarly, we can pass strings (such as the title Featured director) which can be already translated/internationalized on the server-side, avoiding the need to deploy locale configuration files to the front-end.
Similar to fetching data, by traversing the component hierarchy, the API is able to deliver the required configuration values for each module and nothing more or less.
The configuration values for the featured-director component might look like this:
{ modulesettings: { "page": { modules: { "featured-director": { configuration: { class: "alert alert-info", title: "Featured director", titletag: "h3" }, modules: { "director-films": { configuration: { classes: { wrapper: "media", avatar: "mr-3", body: "media-body", films: "row", film: "col-sm-6" }, avatarmaxsize: "100px" }, modules: { "film-actors": { configuration: { classes: { wrapper: "card", image: "card-img-top", body: "card-body", title: "card-title", avatar: "img-thumbnail" } } } } } } } } } } }
Please notice how — because the configuration properties for different modules are nested under each module’s level — these will never collide with each other if having the same name (e.g. property classes from one module will not override property classes from another module), avoiding having to add namespaces for modules.
Higher Degree Of Modularity Achieved In The Application
According to Wikipedia, modularity means:
The degree to which a system’s components may be separated and recombined, often with the benefit of flexibility and variety in use. The concept of modularity is used primarily to reduce complexity by breaking a system into varying degrees of interdependence and independence across and ‘hide the complexity of each part behind an abstraction and interface’.
Being able to update a component just from the server-side, without the need to redeploy JavaScript files, has the consequence of better reusability and maintenance of components. I will demonstrate this by re-imagining how this example coded for React would fare in a component-based API.
Let’s say that we have a <ShareOnSocialMedia> component, currently with two items: <FacebookShare> and <TwitterShare>, like this:
Render <ShareOnSocialMedia>: <ul> <li>Share on Facebook: <FacebookShare url={window.location.href} /></li> <li>Share on Twitter: <TwitterShare url={window.location.href} /></li> </ul>
But then Instagram got kind of cool, so we need to add an item <InstagramShare> to our <ShareOnSocialMedia> component, too:
Render <ShareOnSocialMedia>: <ul> <li>Share on Facebook: <FacebookShare url={window.location.href} /></li> <li>Share on Twitter: <TwitterShare url={window.location.href} /></li> <li>Share on Pinterest: <InstagramShare url={window.location.href} /></li> </ul>
In the React implementation, as it can be seen in the linked code, adding a new component <InstagramShare> under component <ShareOnSocialMedia> forces to redeploy the JavaScript file for the latter one, so then these two modules are not as decoupled as they could be.
In the component-based API, though, we can readily use the relationships among modules already described in the API to couple the modules together. While originally we will have this response:
{ modulesettings: { "share-on-social-media": { modules: { "facebook-share": { configuration: {...} }, "twitter-share": { configuration: {...} } } } } }
After adding Instagram we will have the upgraded response:
{ modulesettings: { "share-on-social-media": { modules: { "facebook-share": { configuration: {...} }, "twitter-share": { configuration: {...} }, "instagram-share": { configuration: {...} } } } } }
And just by iterating all the values under modulesettings["share-on-social-media"].modules, component <ShareOnSocialMedia> can be upgraded to show the <InstagramShare> component without the need to redeploy any JavaScript file. Hence, the API supports the addition and removal of modules without compromising code from other modules, attaining a higher degree of modularity.
Native Client-Side Cache/Data Store
The retrieved database data is normalized in a dictionary structure, and standardized so that, starting from the value on dbobjectids, any piece of data under databases can be reached just by following the path to it as indicated through entries dbkeys, whichever way it was structured. Hence, the logic for organizing data is already native to the API itself.
We can benefit from this situation in several ways. For instance, the returned data for each request can be added into a client-side cache containing all data requested by the user throughout the session. Hence, it is possible to avoid adding an external data store such as Redux to the application (I mean concerning the handling of data, not concerning other features such as the Undo/Redo, the collaborative environment or the time-travel debugging).
Also, the component-based structure promotes caching: the component hierarchy depends not on the URL, but on what components are needed in that URL. This way, two events under /events/1/ and /events/2/ will share the same component hierarchy, and the information of what modules are required can be reutilized across them. As a consequence, all properties (other than database data) can be cached on the client after fetching the first event and reutilized from then on, so that only database data for each subsequent event must be fetched and nothing else.
Extensibility And Re-purposing
The databases section of the API can be extended, enabling to categorize its information into customized subsections. By default, all database object data is placed under entry primary, however, we can also create custom entries where to place specific DB object properties.
For instance, if the component “Films recommended for you” described earlier on shows a list of the logged-in user’s friends who have watched this film under property friendsWhoWatchedFilm on the film DB object, because this value will change depending on the logged-in user then we save this property under a userstate entry instead, so when the user logs out, we only delete this branch from the cached database on the client, but all the primary data still remains:
{ databases: { userstate: { films: { 5: { friendsWhoWatchedFilm: [22, 45] }, } }, primary: { films: { 5: { title: "The Terminator" }, } "people": { 22: { name: "Peter", }, 45: { name: "John", }, }, } } }
In addition, up to a certain point, the structure of the API response can be re-purposed. In particular, the database results can be printed in a different data structure, such as an array instead of the default dictionary.
For instance, if the object type is only one (e.g. films), it can be formatted as an array to be fed directly into a typeahead component:
[ { title: "Star Wars: Episode I - The Phantom Menace", thumbnail: "..." }, { title: "Star Wars: Episode II - Attack of the Clones", thumbnail: "..." }, { title: "The Terminator", thumbnail: "..." }, ]
Support For Aspect-Oriented Programming
In addition to fetching data, the component-based API can also post data, such as for creating a post or adding a comment, and execute any kind of operation, such as logging the user in or out, sending emails, logging, analytics, and so on. There are no restrictions: any functionality provided by the underlying CMS can be invoked through a module — at any level.
Along the component hierarchy, we can add any number of modules, and each module can execute its own operation. Hence, not all operations must necessarily be related to the expected action of the request, as when doing a POST, PUT or DELETE operation in REST or sending a mutation in GraphQL, but can be added to provide extra functionalities, such as sending an email to the admin when a user creates a new post.
So, by defining the component hierarchy through dependency-injection or configuration files, the API can be said to support Aspect-oriented programming, “a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns.”
Recommended reading: Protecting Your Site With Feature Policy
Enhanced Security
The names of the modules are not necessarily fixed when printed in the output, but can be shortened, mangled, changed randomly or (in short) made variable any way intended. While originally thought for shortening the API output (so that module names carousel-featured-posts or drag-and-drop-user-images could be shortened to a base 64 notation, such as a1, a2 and so on, for the production environment), this feature allows to frequently change the module names in the response from the API for security reasons.
For instance, input names are by default named as their corresponding module; then, modules called username and password, which are to be rendered in the client as <input type="text" name="{input_name}"> and <input type="password" name="{input_name}"> respectively, can be set varying random values for their input names (such as zwH8DSeG and QBG7m6EF today, and c3oMLBjo and c46oVgN6 tomorrow) making it more difficult for spammers and bots to target the site.
Versatility Through Alternative Models
The nesting of modules allows to branch out to another module to add compatibility for a specific medium or technology, or change some styling or functionality, and then return to the original branch.
For instance, let’s say the webpage has the following structure:
"module1" modules "module2" modules "module3" "module4" modules "module5" modules "module6"
In this case, we’d like to make the website also work for AMP, however, modules module2, module4 and module5 are not AMP compatible. We can branch these modules out into similar, AMP-compatible modules module2AMP, module4AMP and module5AMP, after which we keep loading the original component hierarchy, so then only these three modules are substituted (and nothing else):
"module1" modules "module2AMP" modules "module3" "module4AMP" modules "module5AMP" modules "module6"
This makes it fairly easy to generate different outputs from a single codebase, adding forks only here and there as needed, and always scoped and restrained to individual modules.
Demonstration Time
The code implementing the API as explained in this article is available in this open-source repository.
I have deployed the PoP API under https://nextapi.getpop.org for demonstration purposes. The website runs on WordPress, so the URL permalinks are those typical to WordPress. As noted earlier, through adding parameter output=json to them, these URLs become their own API endpoints.
The site is backed by the same database from the PoP Demo website, so a visualization of the component hierarchy and retrieved data can be done querying the same URL in this other website (e.g. visiting the https://demo.getpop.org/u/leo/ explains the data from https://nextapi.getpop.org/u/leo/?output=json).
The links below demonstrate the API for cases described earlier on:
The homepage, a single post, an author, a list of posts and a list of users.
An event, filtering from a specific module.
A tag, filtering modules which require user state and filtering to bring only a page from a Single-Page Application.
An array of locations, to feed into a typeahead.
Alternative models for the “Who we are” page: Normal, Printable, Embeddable.
Changing the module names: original vs mangled.
Filtering information: only module settings, module data plus database data.
Example of JSON code returned by the API. (Large preview)
Conclusion
A good API is a stepping stone for creating reliable, easily maintainable and powerful applications. In this article, I have described the concepts powering a component-based API which, I believe, is a pretty good API, and I hope I have convinced you too.
So far, the design and implementation of the API have involved several iterations and taken more than five years — and it’s not completely ready yet. However, it is in a pretty decent state, not ready for production but as a stable alpha. These days, I am still working on it; working on defining the open specification, implementing the additional layers (such as rendering) and writing documentation.
In an upcoming article, I will describe how my implementation of the API works. Until then, if you have any thoughts about it — regardless whether positive or negative — I would love to read your comments below.
(rb, ra, yk, il)
0 notes
Text
In CSI Cyber, Russell made that fake dating profile under the guise of "dolla bill" for his initials DB, BUT, BUT, what did Russell give Julie Finlay in Seeing Red on her first shift as a form of payment? A single dollar bill. Holy parallels and head canons ahoy mates!
#lmao#why didn't I see this before#holy hell#db russell#julie finlay#maybe thats why he picked it#csi parallels
5 notes
·
View notes
Note
serana
serana is one of the most well-rounded skyrim characters, and yet i must warn: headcanons ahoy
despite her age, she is still sheltered: she was turned into a vampire fairly young after growing up in a tower with no one but her parents and books for company - and then almost immediately got put into ‘stasis’ when harkon started to dip off the deep end and valerica sequestered serana away. this results in serana being INCREDIBLY old and INCREDIBLY well-read, but... she has little lived experience. there are so many things that are new and amazing to her, and she frequently expresses that wonder.
she loves her family and accepting that they were her abusers was really fucking hard for her: they were a ‘real family’ once, to her mind. and i don’t know if her father was always a rat bastard, but valerica at least shared a lot of quality time with her daughter. though evidence would suggest that both her parents isolated her even before the Vampire Times, which leads me down suspicion paths of overprotective tendencies and other emotional abuse of forcing all aspects of her life to rely on her parents. but she loves them - how could she not? and coming to terms with the life they forced on her and the fact that both of them were tbh REALLY bad parents in their own ways (valerica less terrible than harkon, but.) is a bitter pill. having to kill harkon... was difficult. she still needs time to make her peace with it.
she didn’t want to be a vampire, but she doesn’t necessarily want to let it go, either: this is tied heavily to her family problems, but also b/c i don’t necessarily think she sees all vampires as irredeemable monsters (which, y’know, is influenced by the fact that she IS a vampire, but also because not all vampires chose the life when they were bitten or thralled or otherwise) - she was essentially forced into becoming a daughter of coldharbour by her parents. they placed heavy expectations on her, and as a young woman who had only known her parents and volkihar castle all her life, could she refuse? could she really say no? when she had been raised to understand that her parents were always right? and hand-in-hand with coming to terms with her parents’ abuse, it takes her a bit longer to deal with the repressed trauma of being turned into a vampire against her will. to realize that she was young and she made no conscious choice to become one - that the choice was made for her, and she pressed into its mold regardless of her input. but she’s also been one for a Long Fucking Time, and it’s the last reminder of her ‘family’ that she has left. to stop being a vampire is a momentous and extremely personal choice, and one that i don’t think she’d make on the db’s whim.
she’s very nice to the db and has a great sense of humor but she’s also socially awkward: the db (i mean, unless you decided to be an asshole to her over the course of dawnguard, in which case whaddahale is wrong with you) is like, her closest friend. the first friend she made in this world, and maybe the only one she has at the moment. she’s very fond of them - they’ve helped her, and defended her, and she’s defended them (against valerica and harkon, the two hardest opponents for serana to actually fight) - but dealing with more than a few people at a time, or even like the average person? she kinda fucking sucks at small talk, and even though she has lordly manners and diplomacy trained into her, she just... gets gummed up sometimes. she gets quiet and slips into ‘listening’ mode when she doesn’t know what else to say; social machine broke.
17 notes
·
View notes
Video
youtube
Download amino to check out extra footage from this and other videos! https://ift.tt/2FEyJ1S Hopefully this video helps someone in some way. PATREON 💙: https://ift.tt/2goq6fj DAILY LIVE STREAMS: https://ift.tt/2FGw5bH TWITTER: http://www.twitter.com/HeyoDamo BACKGROUND MUSIC: "dance" By Dylla x Bonus Points https://ift.tt/2FKlFKf ------------------------------------------------------------------------------ TWITTER: http://www.twitter.com/HeyoDamo FACEBOOK: https://ift.tt/1Ebb6s3 INSTAGRAM: https://ift.tt/1zTznw5 SNAPCHAT: HeyoDamo (barely use it tho) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ THANK YOU SO MUCH TO MY PATRONS FOR SUPPORTING ME! Kibaki Kyarako Sean Francis (Tanukiami) Chazzy Wolf Failed Offerings Alban Nightfury Rojan Easo Snooper Logan murrow Ikideruuu BooshieBoo Casandra Wagner SolomonRivers Dibbo Mika Glitch maddy @MalaikaWolf Sax Andy White SceptiX Odin Wolf BlurpleDutchie Kiva the Birb Toxic Moonlight Cartmanland Jonnie Z SchizoBug Daddi Gabe Pips Ahoy! lichtwolf Koma Viper FurtasticStudios Lachlan Halliwell Hazridge Skylar McElroy Worgon James Hefel AntiBlueFox Ragehound Dalmy NovaCat Nos Hyena DB Husky Yuke Weiß Zed Shirogane Flamingo My Mango ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ by HeyoDamo
2 notes
·
View notes