[{"data":1,"prerenderedAt":316},["ShallowReactive",2],{"application-guide-nav":3,"application-guide-node-red-design-patterns":101},[4,11,15,21,24,30,35,41,46,52,57,63,68,74,79,85,91,97],{"guide":5,"slug":6,"title":7,"navOrder":8,"blurb":9,"path":10},"flowfuse","overview","Overview",1,"Turn an app idea into FlowFuse pieces you can name and say in one sentence.","\u002Fapplication-guide\u002Fflowfuse\u002Foverview\u002F",{"guide":12,"slug":6,"title":7,"navOrder":8,"blurb":13,"path":14},"node-red","Turn an architecture sentence into a clean flow shape you can read at a glance.","\u002Fapplication-guide\u002Fnode-red\u002Foverview\u002F",{"guide":5,"slug":16,"title":17,"navOrder":18,"blurb":19,"path":20},"foundations","Foundations",2,"The foundation to build on: what FlowFuse is, its core pieces, and how code is shared across teams.","\u002Fapplication-guide\u002Fflowfuse\u002Ffoundations\u002F",{"guide":12,"slug":16,"title":17,"navOrder":18,"blurb":22,"path":23},"The handful of concepts you need to build with Node-RED, and how they fit together.","\u002Fapplication-guide\u002Fnode-red\u002Ffoundations\u002F",{"guide":5,"slug":25,"title":26,"navOrder":27,"blurb":28,"path":29},"app-delivery-methods","App delivery methods",3,"Two different units of code, delivered two ways: ship the whole app, or publish one reusable piece.","\u002Fapplication-guide\u002Fflowfuse\u002Fapp-delivery-methods\u002F",{"guide":12,"slug":31,"title":32,"navOrder":27,"blurb":33,"path":34},"flow-shape","Flow shape","Turn an architecture sentence into a flow you can read at a glance.","\u002Fapplication-guide\u002Fnode-red\u002Fflow-shape\u002F",{"guide":5,"slug":36,"title":37,"navOrder":38,"blurb":39,"path":40},"hardware-apps","Hardware apps",4,"The three shapes a FlowFuse app takes when it runs on a device.","\u002Fapplication-guide\u002Fflowfuse\u002Fhardware-apps\u002F",{"guide":12,"slug":42,"title":43,"navOrder":38,"blurb":44,"path":45},"design-patterns","Design patterns","The moves that turn a flow into something you can reuse, test and hand off.","\u002Fapplication-guide\u002Fnode-red\u002Fdesign-patterns\u002F",{"guide":5,"slug":47,"title":48,"navOrder":49,"blurb":50,"path":51},"software-apps","Software apps",5,"The three shapes a FlowFuse app takes when it runs on the platform.","\u002Fapplication-guide\u002Fflowfuse\u002Fsoftware-apps\u002F",{"guide":12,"slug":53,"title":54,"navOrder":49,"blurb":55,"path":56},"handling-data","Handling data","Sort data by what it is for before you tune anything.","\u002Fapplication-guide\u002Fnode-red\u002Fhandling-data\u002F",{"guide":5,"slug":58,"title":59,"navOrder":60,"blurb":61,"path":62},"data-plane","Data plane",6,"Before you pick where things run, decide how data is handled.","\u002Fapplication-guide\u002Fflowfuse\u002Fdata-plane\u002F",{"guide":12,"slug":64,"title":65,"navOrder":60,"blurb":66,"path":67},"operating-flows","Operating flows","Four operational habits that keep a flow running when the real world pushes back.","\u002Fapplication-guide\u002Fnode-red\u002Foperating-flows\u002F",{"guide":5,"slug":69,"title":70,"navOrder":71,"blurb":72,"path":73},"architectures","Architectures",7,"The same building blocks, arranged for where they run.","\u002Fapplication-guide\u002Fflowfuse\u002Farchitectures\u002F",{"guide":12,"slug":75,"title":76,"navOrder":71,"blurb":77,"path":78},"worked-example","Worked example","The shape rules and patterns applied end to end.","\u002Fapplication-guide\u002Fnode-red\u002Fworked-example\u002F",{"guide":5,"slug":80,"title":81,"navOrder":82,"blurb":83,"path":84},"it-architectures","IT architectures",8,"Hosting the platform and serving the wider organisation.","\u002Fapplication-guide\u002Fflowfuse\u002Fit-architectures\u002F",{"guide":5,"slug":86,"title":87,"navOrder":88,"blurb":89,"path":90},"ot-architectures","OT architectures",9,"How FlowFuse deploys in operational-technology environments, near the equipment.","\u002Fapplication-guide\u002Fflowfuse\u002Fot-architectures\u002F",{"guide":5,"slug":92,"title":93,"navOrder":94,"blurb":95,"path":96},"iiot-architectures","IIoT architectures",10,"Distributed edge nodes, each doing one small job, feeding one central system.","\u002Fapplication-guide\u002Fflowfuse\u002Fiiot-architectures\u002F",{"guide":5,"slug":75,"title":76,"navOrder":98,"blurb":99,"path":100},11,"OEE, end to end — two packages, two connections.","\u002Fapplication-guide\u002Fflowfuse\u002Fworked-example\u002F",{"id":102,"title":43,"blocks":103,"blurb":44,"extension":312,"guide":12,"meta":313,"navOrder":38,"slug":42,"stem":314,"__hash__":315},"applicationGuide\u002Fapplication-guide\u002Fnode-red\u002F04-design-patterns.yml",[104,108],{"type":105,"kicker":106,"body":107},"prose","Design patterns — start here","The moves that turn a flow into something you can reuse, test and hand off. Find the seams, pick the lightest level of reuse, keep the UI and logic apart, and hold state in context — then scale only when one instance proves it must.",{"type":109,"panels":110},"tabs",[111,147,176,209,246,277],{"label":112,"kicker":113,"kickerNote":114,"diagramTitle":115,"diagram":116,"summary":140,"useWhen":141,"howHeading":142,"how":143,"callout":144},"Find the seams","Name the structure","Most spaghetti is three or four components never named; draw a box around each.","Name the stages hiding inside one instance",{"lanes":117},[118,126,128],{"label":119,"tone":120,"nodes":121},"Before","neutral",[122],{"title":123,"sub":124,"tone":125},"one flow, no seams","nothing to reuse or hand off","muted",{"link":127},"draw the boxes",{"label":129,"tone":130,"nodes":131},"After","it",[132,134,136,138],{"title":133,"tone":130},"Ingest",{"title":135,"tone":130},"Normalize",{"title":137,"tone":130},"Enrich",{"title":139,"tone":130},"Publish","A giant flow is bad because it has no seams. Most spaghetti is three or four well-defined components that were never named.","You cannot reuse, test, or hand off a piece of a blob, and any edit means reading the whole tab. Naming the structure is the fix, not tidier wires.","How to apply it","Look for a repeated cluster, a logical stage (ingest, normalize, enrich, publish), a bounded responsibility, or a reuse magnet. Draw a box around each and extract it.",{"tone":145,"text":146},"good","Splitting one tab into ingestion, processing, storage, and presentation.",{"label":148,"kicker":149,"kickerNote":150,"diagramTitle":151,"diagram":152,"summary":170,"useWhen":171,"howHeading":142,"how":172,"callout":173},"Levels of reuse","Pick the lowest rung","link in\u002Fout → link call → subflow → packaged node; each rung costs more than the one below.","Pick the lowest rung that solves it",{"lanes":153},[154],{"label":155,"tone":120,"nodes":156},"Cost rises with each rung",[157,160,163,166],{"title":158,"sub":159,"tone":125},"1 · link in \u002F out","tidiness only",{"title":161,"sub":162},"2 · link call","shared, returning service",{"title":164,"sub":165,"tone":130},"3 · subflow","per-instance or reuse",{"title":167,"sub":168,"tone":169},"4 · palette node","real code, distribution","strong","When you find structure there are levels of extraction: link in and out, then link call, then subflow, then a packaged node. Pick the lowest one that solves the problem.","Each rung costs more than the one below it. Reaching straight for a subflow or a custom node when a link call would do adds weight you do not need.","Link in and out for tidiness and tab-to-tab routing. Link call for a shared returning service. Subflow when it needs per-instance config or cross-instance reuse. Palette node for real code or distribution.",{"tone":174,"text":175},"watch","Link nodes are organization, not modularity. Do not mistake tidiness for a reusable unit.",{"label":177,"kicker":178,"kickerNote":179,"diagramTitle":180,"diagram":181,"summary":204,"useWhen":205,"howHeading":142,"how":206,"callout":207},"Decouple frontend & backend","UI ↔ logic like client ↔ server","The backend sends a display-ready view-model; the frontend emits intent (action + payload).","Frontend and backend, two instances, one contract",{"lanes":182},[183,189,191,196,198],{"label":184,"tone":120,"nodes":185},"Frontend · renders + emits",[186],{"title":187,"sub":188},"Node-RED","renders + emits",{"link":190},"intent (what the user wants) ↕ view-model (display-ready state)",{"label":192,"tone":130,"nodes":193},"Backend · holds the truth",[194],{"title":187,"sub":195,"tone":130},"holds the truth",{"link":197},"reads · writes",{"label":199,"tone":120,"nodes":200},"Store",[201],{"title":202,"sub":203,"tone":125},"SQL database","records","Treat the boundary between the Dashboard and your flow logic like a client and server API. The frontend renders state and emits intent; the backend holds the truth.","The UI and the logic change at different rates. When you build payloads inside templates or cram logic next to a widget, every change touches both.","The backend sends a finished, display-ready view-model. The frontend emits a consistent intent message, an action plus a payload. Templates bind and emit; they never fetch, transform, or decide.",{"tone":145,"text":208},"Redesigning the whole dashboard without touching a single business-logic node.",{"label":210,"kicker":211,"kickerNote":212,"diagramTitle":213,"diagram":214,"summary":241,"useWhen":242,"howHeading":142,"how":243,"callout":244},"Use the context store","Context is shared memory","Hold the object in one place at the narrowest scope; messages are verbs, context is state.","Messages are verbs, context is nouns",{"lanes":215},[216,225,227,233,235],{"label":217,"tone":120,"nodes":218},"Event",[219,222],{"title":220,"sub":221},"event","a reading arrived",{"title":223,"sub":224},"node","recompute",{"link":226},"write",{"label":228,"tone":130,"nodes":229},"Context store",[230],{"title":231,"sub":232,"tone":130},"context store","assets.\u003Cid>, oee.line1",{"link":234},"read the one key it needs",{"label":236,"tone":120,"nodes":237},"Consumer",[238],{"title":239,"sub":240},"another node","reads one key","Context is shared memory with a defined scope. It holds a logical object in one place instead of threading it through wires. Messages are verbs; context is nouns.","If you pass the same fat object through fifteen nodes just to move it, that is the job context exists to do. Wire gymnastics to avoid storing a value is the real anti-pattern.","Store the object once under a namespaced key at the narrowest scope that works (node, then flow, then global). Each node reads the one key it needs, and a persistent store holds anything that must survive a restart.",{"tone":174,"text":245},"One writer per key, serialize concurrent updates, and keep enough on the wire to stay debuggable.",{"label":247,"kicker":248,"kickerNote":249,"diagram":250,"summary":272,"useWhen":273,"howHeading":142,"how":274,"callout":275},"Config: env vars vs persisted context","Static config vs runtime config","Env vars set at deploy (read-only to the flow); persisted context for anything a user edits.",{"lanes":251},[252,261],{"label":253,"tone":120,"nodes":254},"Env var, set at deploy",[255,258],{"title":256,"sub":257,"tone":125},"BROKER_HOST","baked, read-only",{"title":259,"sub":260,"tone":125},"to change it","edit env, then redeploy",{"label":262,"tone":130,"nodes":263},"Persisted context, changed by a user",[264,267,269],{"title":265,"sub":266,"tone":130},"UI edits","a form or button",{"title":231,"sub":268,"tone":130},"live config",{"title":270,"sub":271,"tone":130},"flow at run","reads current","Two different kinds of configuration. Static config changes per environment and is set at deploy: broker host, DB connection. Runtime config changes while running, by a user, with no redeploy.","Env vars are resolved at deploy time and are read-only to the running flow. The moment a user needs to change what the flow operates on, an env var forces a developer and a redeploy.","Keep static config in env vars or a config node. Put user-editable config in persisted context (or a config file), edited through the UI via an intent message, and read by the flow at execution time.",{"tone":174,"text":276},"If a value would ever be changed through a button or form, it is not an env var.",{"label":278,"kicker":279,"kickerNote":280,"diagramTitle":281,"diagram":282,"summary":307,"useWhen":308,"howHeading":142,"how":309,"callout":310},"Generating flows (Admin API)","Deploy flows as JSON","Populate link arrays with node ids and l:true; the editor's quiet fixes won't happen over the API.","Emit JSON, deploy via the Admin API",{"lanes":283,"note":306},[284,290,292,296,298],{"label":285,"tone":120,"nodes":286},"You emit it",[287],{"title":288,"sub":289,"tone":125},"flow JSON","you emit it",{"link":291},"POST \u002Fflows",{"label":293,"tone":130,"nodes":294},"Admin API",[295],{"title":293,"sub":291,"tone":130},{"link":297},"deploys to",{"label":299,"tone":300,"nodes":301},"Runtimes","ot",[302,304],{"title":187,"sub":303,"tone":300},"runtime",{"title":187,"sub":305,"tone":300},"edge device","links populated by node id (not name), l:true, x = LEFT + width\u002F2, respond once on msg.error","Emitting flows as JSON and deploying via the Admin API. The editor quietly fixes mistakes that a programmatic deploy does not, so the failure classes are different.","API-deployed flows look right but hang, double-respond, loop, or read as spaghetti when the things the editor papers over are left undone.","Populate link arrays with target node ids (name matching is editor-only) and set l:true. Skip success formatters when msg.error is set so you respond once. Never let a tab-wide catch re-dispatch a response-layer error. Compute x as LEFT + width\u002F2.",{"tone":174,"text":311},"Empty link arrays are the top reason an API-deployed flow does nothing.","yml",{},"application-guide\u002Fnode-red\u002F04-design-patterns","nx8L-sWMXtPcKrEaRz9Sp-x6-fYgQkvD_cZiB4TChGk",1786132521773]