site and webhooks packages stubbed
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
# V2 - Attempt 2
|
||||
|
||||
|
||||
Looking to follow the [CQRS](https://martinfowler.com/bliki/CQRS.html) pattern.
|
||||
|
||||
The database will follow the [event sourcing](https://martinfowler.com/eaaDev/EventSourcing.html) database pattern.
|
||||
All events (or commands) will be stored in a respective event series, and all database reads will be from views that are projections, reductions, aggregations of those event series.
|
||||
|
||||
|
||||
## Application/directory structure
|
||||
- /internal
|
||||
- /site: website
|
||||
- /webhooks: webhooks for platform events
|
||||
- /domains: packages for each domain
|
||||
- /store_events: storing and events
|
||||
- /platforms: ecommerce platform domains
|
||||
- /tiktok: interface with tiktok
|
||||
- ... etc
|
||||
- ... etc
|
||||
|
||||

|
||||
|
||||
|
||||
## Architectural and Software Diagrams
|
||||
|
||||
|
||||
### Event Sourcing Architecture
|
||||

|
||||
|
||||
|
||||
### Event Structure
|
||||
|
||||

|
||||
|
||||
|
||||
### Store Event Database Tables
|
||||
|
||||

|
||||
|
||||
|
||||
### All Diagrams
|
||||
|
||||
All diagrams are stored in the [diagrams](./diagrams) directory
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 20 KiB |
@@ -0,0 +1,49 @@
|
||||
@startuml
|
||||
|
||||
|
||||
cloud {
|
||||
[Web site]
|
||||
}
|
||||
|
||||
cloud {
|
||||
[Tiktok API]
|
||||
[Etsy API]
|
||||
[Etc APIs]
|
||||
}
|
||||
|
||||
node "http server" {
|
||||
frame "site" {
|
||||
[REST API]
|
||||
[HTML Templates]
|
||||
}
|
||||
frame "webhooks" {
|
||||
[Tiktok Hook]
|
||||
[Etsy Hook]
|
||||
[Etc Hooks]
|
||||
}
|
||||
}
|
||||
|
||||
node "domains" {
|
||||
[store events]
|
||||
[tiktok]
|
||||
[etsy]
|
||||
[etc]
|
||||
}
|
||||
|
||||
[Web site] -- [REST API]
|
||||
[REST API] -- [HTML Templates]
|
||||
[REST API] -- [store events]
|
||||
[REST API] -- [tiktok]
|
||||
[REST API] -- [etsy]
|
||||
[REST API] -- [etc]
|
||||
|
||||
[Tiktok API] -- [Tiktok Hook]
|
||||
[Etsy API] -- [Etsy Hook]
|
||||
[Etc APIs] -- [Etc Hooks]
|
||||
|
||||
[Tiktok Hook] -- [tiktok]
|
||||
[Etsy Hook] -- [etsy]
|
||||
[Etc Hooks] -- [etc]
|
||||
|
||||
|
||||
@enduml
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="390px" preserveAspectRatio="none" style="width:292px;height:390px;" version="1.1" viewBox="0 0 292 390" width="292px" zoomAndPan="magnify"><defs><filter height="300%" id="f181xfa91hfwdr" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><!--MD5=[d841c4c2f84d4e18d352f800547f43fc]
|
||||
cluster platform--><path d="M207.4101,290.7887 C211.7845,283.0528 217.0217,280.9365 223.6689,288.3495 C226.7042,281.611 233.8131,279.6104 238.5614,286.4741 C244.6464,279.7464 251.8459,281.5338 254.6263,289.9741 C261.7113,281.3619 267.2181,281.78 272.1849,291.954 C276.6125,296.2279 277.219,299.7576 271.0513,302.9381 C277.8145,306.9578 278.8998,311.0639 272.1982,316.2441 C278.8575,318.4683 279.456,322.3798 274.8287,327.2915 C280.5151,331.9934 278.5896,337.3451 272.3701,339.6852 C278.3415,342.7954 278.9048,348.3463 273.0139,352.073 C278.2453,357.2174 277.5663,362.7407 270.0414,364.9375 C266.4511,372.8768 259.3404,373.0132 254.6828,366.0366 C251.4808,375.4858 245.272,376.1696 238.8691,369.101 C233.7762,376.4902 226.1736,374.4101 222.9495,367.0798 C216.4398,375.216 210.3084,373.7899 206.2037,364.7368 C200.3841,360.9853 199.0186,356.0973 205.6657,351.8263 C200.7098,348.563 200.3242,343.885 205.1097,340.1575 C198.7812,338.0586 198.8138,332.4768 203.7132,328.9756 C197.4907,324.3591 199.5192,318.4844 205.6342,315.6457 C199.8774,311.8173 200.5234,308.2951 205.4265,304.2721 C200.5254,300.0621 200.3627,293.2409 207.4101,290.7887 " fill="#FFFFFF" style="stroke: #000000; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="61" x="207.5" y="309.9659">platform</text><rect fill="#FEFECE" filter="url(#f181xfa91hfwdr)" height="36.3441" style="stroke: #A80036; stroke-width: 1.5;" width="47" x="214.5" y="320"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="33" x="221.5" y="334.8281">Order</text><line style="stroke: #A80036; stroke-width: 1.5;" x1="215.5" x2="260.5" y1="340.3441" y2="340.3441"/><rect fill="#FEFECE" filter="url(#f181xfa91hfwdr)" height="36.3441" style="stroke: #A80036; stroke-width: 1.5;" width="45" x="57.5" y="8.5"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="31" x="64.5" y="23.3281">Event</text><line style="stroke: #A80036; stroke-width: 1.5;" x1="58.5" x2="101.5" y1="28.8441" y2="28.8441"/><rect fill="#FEFECE" filter="url(#f181xfa91hfwdr)" height="36.3441" style="stroke: #A80036; stroke-width: 1.5;" width="92" x="34" y="104.5"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="78" x="41" y="119.3281">HTTP Request</text><line style="stroke: #A80036; stroke-width: 1.5;" x1="35" x2="125" y1="124.8441" y2="124.8441"/><rect fill="#FEFECE" filter="url(#f181xfa91hfwdr)" height="36.3441" style="stroke: #A80036; stroke-width: 1.5;" width="62" x="6" y="200.5"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="48" x="13" y="215.3281">Headers</text><line style="stroke: #A80036; stroke-width: 1.5;" x1="7" x2="67" y1="220.8441" y2="220.8441"/><rect fill="#FEFECE" filter="url(#f181xfa91hfwdr)" height="36.3441" style="stroke: #A80036; stroke-width: 1.5;" width="42" x="103" y="200.5"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="28" x="110" y="215.3281">Body</text><line style="stroke: #A80036; stroke-width: 1.5;" x1="104" x2="144" y1="220.8441" y2="220.8441"/><rect fill="#FEFECE" filter="url(#f181xfa91hfwdr)" height="82.8241" style="stroke: #A80036; stroke-width: 1.5;" width="110" x="69" y="296.5"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="96" x="76" y="311.3281">event description</text><line style="stroke: #A80036; stroke-width: 1.5;" x1="70" x2="178" y1="316.8441" y2="316.8441"/><text fill="#000000" font-family="sans-serif" font-size="10" lengthAdjust="spacingAndGlyphs" textLength="25" x="75" y="331.5341">store</text><text fill="#000000" font-family="sans-serif" font-size="10" lengthAdjust="spacingAndGlyphs" textLength="9" x="75" y="345.1541">id</text><text fill="#000000" font-family="sans-serif" font-size="10" lengthAdjust="spacingAndGlyphs" textLength="52" x="75" y="358.7741">timestamp</text><text fill="#000000" font-family="sans-serif" font-size="10" lengthAdjust="spacingAndGlyphs" textLength="26" x="75" y="372.3941">order</text><!--MD5=[2ccc1eaed605fa0e0cf1bdb00c98ccc0]
|
||||
link Event to HTTPRequest--><path d="M80,44.74 C80,59.98 80,82.31 80,98.96 " fill="none" id="Event->HTTPRequest" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="80,104.37,84,95.37,80,99.37,76,95.37,80,104.37" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[67da588534e36a000f2f212cb4573958]
|
||||
reverse link HTTPRequest to Headers--><path d="M66.53,152.95 C59.4,168.54 50.88,187.15 44.84,200.37 " fill="none" id="HTTPRequest<-Headers" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="72.11,140.74,65.974,144.528,67.1106,151.649,73.2466,147.861,72.11,140.74" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[d58850b6fb58ba577537fc7ea6e0971f]
|
||||
reverse link HTTPRequest to Body--><path d="M93.78,152.95 C101.08,168.54 109.79,187.15 115.98,200.37 " fill="none" id="HTTPRequest<-Body" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="88.07,140.74,86.9859,147.8691,93.1497,151.6118,94.2338,144.4827,88.07,140.74" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[71037632cbcca795a013e24b2d66ab25]
|
||||
reverse link Body to EventDescription--><path d="M124,249.62 C124,263.81 124,280.95 124,296.28 " fill="none" id="Body<-EventDescription" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="124,236.59,120,242.59,124,248.59,128,242.59,124,236.59" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[cdbb0c1ca42aa84d86e03a93bd99d875]
|
||||
reverse link EventDescription to Order--><path d="M199.4,338 C204.3,338 209.21,338 214.12,338 " fill="none" id="EventDescription<-Order" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="none" points="199.22,345,179.22,338,199.22,331,199.22,345" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[20eda4c0919a24efe2526ecd58e999de]
|
||||
@startuml
|
||||
|
||||
|
||||
object Event
|
||||
object "HTTP Request" as HTTPRequest
|
||||
object Headers
|
||||
object Body
|
||||
object "event description" as EventDescription
|
||||
cloud platform {
|
||||
object Order
|
||||
}
|
||||
|
||||
|
||||
Event - -> HTTPRequest
|
||||
HTTPRequest *- - Headers
|
||||
HTTPRequest *- - Body
|
||||
Body *- - EventDescription
|
||||
|
||||
EventDescription : store
|
||||
EventDescription : id
|
||||
EventDescription : timestamp
|
||||
EventDescription : order
|
||||
|
||||
EventDescription <|- Order
|
||||
|
||||
|
||||
|
||||
@enduml
|
||||
|
||||
PlantUML version 1.2020.02(Sun Mar 01 03:22:07 MST 2020)
|
||||
(GPL source distribution)
|
||||
Java Runtime: OpenJDK Runtime Environment
|
||||
JVM: OpenJDK 64-Bit Server VM
|
||||
Java Version: 21.0.9+10-Ubuntu-124.04
|
||||
Operating System: Linux
|
||||
Default Encoding: UTF-8
|
||||
Language: en
|
||||
Country: US
|
||||
--></g></svg>
|
||||
|
After Width: | Height: | Size: 7.4 KiB |
@@ -0,0 +1,28 @@
|
||||
@startuml
|
||||
|
||||
|
||||
object Event
|
||||
object "HTTP Request" as HTTPRequest
|
||||
object Headers
|
||||
object Body
|
||||
object "event description" as EventDescription
|
||||
cloud platform {
|
||||
object Order
|
||||
}
|
||||
|
||||
|
||||
Event --> HTTPRequest
|
||||
HTTPRequest *-- Headers
|
||||
HTTPRequest *-- Body
|
||||
Body *-- EventDescription
|
||||
|
||||
EventDescription : store
|
||||
EventDescription : id
|
||||
EventDescription : timestamp
|
||||
EventDescription : order
|
||||
|
||||
EventDescription <|- Order
|
||||
|
||||
|
||||
|
||||
@enduml
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="331px" preserveAspectRatio="none" style="width:629px;height:331px;" version="1.1" viewBox="0 0 629 331" width="629px" zoomAndPan="magnify"><defs><filter height="300%" id="f1421eyxhylf5w" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><!--MD5=[e8f3788ad7b97e8de68b4194558bc2a1]
|
||||
cluster db--><path d="M362.5,34.5 C362.5,24.5 490,24.5 490,24.5 C490,24.5 617.5,24.5 617.5,34.5 L617.5,309 C617.5,319 490,319 490,319 C490,319 362.5,319 362.5,309 L362.5,34.5 " fill="#FFFFFF" filter="url(#f1421eyxhylf5w)" style="stroke: #000000; stroke-width: 1.5;"/><path d="M362.5,34.5 C362.5,44.5 490,44.5 490,44.5 C490,44.5 617.5,44.5 617.5,34.5 " fill="none" style="stroke: #000000; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="93" x="443.5" y="61.4659">db (postgres)</text><!--MD5=[e2a75efa8a0f57c2afae8bf75386f79c]
|
||||
cluster views--><rect fill="#FFFFFF" filter="url(#f1421eyxhylf5w)" height="93" style="stroke: #000000; stroke-width: 1.5;" width="207" x="386.5" y="85.5"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="39" x="470.5" y="102.4659">views</text><!--MD5=[358ab9ee084784a6364725e117c35e22]
|
||||
entity eventtable--><rect fill="#FEFECE" filter="url(#f1421eyxhylf5w)" height="39.0679" style="stroke: #000000; stroke-width: 1.5;" width="93" x="381" y="258"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="73" x="391" y="282.9659">raw events</text><!--MD5=[d6856b040a1663a798ad7a4fdedcc4e9]
|
||||
entity trigger--><ellipse cx="561.5" cy="268" fill="#FEFECE" filter="url(#f1421eyxhylf5w)" rx="12" ry="12" style="stroke: #A80036; stroke-width: 2.0;"/><polygon fill="#A80036" points="557.5,256,563.5,251,561.5,256,563.5,261,557.5,256" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="47" x="538" y="298.9659">trigger</text><!--MD5=[9a3c926baf05f907617b421b7de43814]
|
||||
entity virtualViews--><rect fill="#FEFECE" filter="url(#f1421eyxhylf5w)" height="39.0679" style="stroke: #000000; stroke-width: 1.5;" width="63" x="403" y="123.5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="43" x="413" y="148.4659">virtual</text><!--MD5=[76cb7dc50c7f0087f20694a01d35754d]
|
||||
entity materialViews--><rect fill="#FEFECE" filter="url(#f1421eyxhylf5w)" height="39.0679" style="stroke: #000000; stroke-width: 1.5;" width="76" x="501.5" y="123.5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="56" x="511.5" y="148.4659">material</text><!--MD5=[9205152578b4c4885530cd24a645560f]
|
||||
entity platforms--><path d="M15.0004,252.3962 C17.9147,246.3284 23.6394,244.9637 28.0382,250.7656 C31.5671,246.5538 36.1781,246.049 38.9921,251.5702 C41.0232,244.8973 46.9531,244.3438 50.8428,249.7463 C53.4113,243.095 58.809,242.8404 62.5198,248.6783 C66.1639,241.9808 71.3092,243.5149 74.9427,248.6894 C77.4151,242.4054 81.6703,241.8911 86.3847,246.2876 C90.1319,240.7147 93.8086,240.9119 97.1528,246.6706 C100.2614,242.0476 105.8153,241.0407 108.6255,246.9942 C112.5651,240.4518 118.4868,240.0524 122.2963,247.1247 C125.976,243.1387 129.934,243.257 132.0269,248.7532 C136.347,242.1507 142.1109,243.7875 145.2468,249.977 C149.0378,244.9482 154.2668,245.8931 156.4795,251.6247 C159.9318,247.1424 165.3293,247.8174 167.8447,252.7549 C170.8599,246.6291 176.8692,246.2529 180.9339,251.5526 C188.0624,253.3067 190.8687,259.666 184.8734,265.2066 C192.4385,266.7439 195.0805,273.0861 189.3634,278.9597 C193.5784,285.0838 190.7271,290.5938 183.529,291.3388 C188.7123,296.6125 187.3826,302.5082 180.1146,304.5682 C176.8141,311.1678 172.1888,311.1065 167.6075,305.9283 C164.3979,310.6024 161.0348,311.6753 157.2106,306.5826 C153.9444,312.8946 148.5571,312.928 144.9224,306.9511 C140.2535,312.7928 135.6856,311.312 132.4473,305.4288 C129.7245,311.1787 125.4397,312.2033 121.1054,307.1152 C117.8045,312.3325 112.4506,313.2367 108.8567,307.441 C106.0404,312.1853 101.1046,311.8268 98.4812,307.2269 C94.9023,312.8611 88.1829,313.9539 84.8658,306.945 C81.853,311.5192 76.508,311.8046 73.685,306.7251 C70.4695,312.0285 66.0073,311.8158 62.3445,307.2386 C57.6569,311.8644 53.0502,311.0897 50.4689,304.9212 C47.1294,310.5761 42.5034,310.3521 39.114,304.9726 C34.666,311.1463 30.733,310.0703 26.4257,304.8163 C22.6843,311.0862 19.1749,310.1979 15.0194,305.237 C7.085,302.3704 4.7998,297.6757 10.7615,290.7304 C4.9824,289.3668 3.4157,283.8261 7.1171,279.4104 C1.9534,273.9856 2.9473,266.6986 11.414,265.4774 C7.0485,259.4079 7.355,255.1728 15.0004,252.3962 " fill="#FEFECE" style="stroke: #000000; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="151" x="21" y="273.4659">ecommerce platforms:</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="63" x="21" y="292.5339">A, B, C, ...</text><!--MD5=[b1a7855e6585e68a7a22c3cffb63dc0f]
|
||||
entity webhook--><path d="M267,256 L267,280 M267,268 L284,268 " fill="none" filter="url(#f1421eyxhylf5w)" style="stroke: #A80036; stroke-width: 2.0;"/><ellipse cx="296" cy="268" fill="#FEFECE" filter="url(#f1421eyxhylf5w)" rx="12" ry="12" style="stroke: #A80036; stroke-width: 2.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="60" x="257.5" y="298.9659">webhook</text><!--MD5=[23cab040c855f106073a9554ba0a8f73]
|
||||
link platforms to webhook--><path d="M187.25,277.5 C210.3,277.5 233.72,277.5 252.12,277.5 " fill="none" id="platforms->webhook" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="257.17,277.5,248.17,273.5,252.17,277.5,248.17,281.5,257.17,277.5" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="34" x="205.25" y="270.397">event</text><!--MD5=[83035e2f9789a9fa293c560fea234ed5]
|
||||
link webhook to eventtable--><path d="M317.53,277.5 C334.5,277.5 356.17,277.5 375.78,277.5 " fill="none" id="webhook->eventtable" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="380.99,277.5,371.99,273.5,375.99,277.5,371.99,281.5,380.99,277.5" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="27" x="335.75" y="270.397">save</text><!--MD5=[f0230ff149e87d499b070d64117e03f8]
|
||||
link eventtable to trigger--><path d="M474.37,277.5 C493.79,277.5 515.7,277.5 532.7,277.5 " fill="none" id="eventtable->trigger" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="537.8,277.5,528.8,273.5,532.8,277.5,528.8,281.5,537.8,277.5" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="27" x="492.5" y="270.397">read</text><!--MD5=[860cd78a7786da8322932b9ab26eba23]
|
||||
reverse link materialViews to trigger--><path d="M543.46,167.84 C547.38,191.48 553.37,227.55 557.36,251.56 " fill="none" id="materialViews<-trigger" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="542.61,162.7,540.1306,172.2317,543.425,167.6331,548.0236,170.9276,542.61,162.7" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="43" x="551.5" y="209.397">update</text><!--MD5=[2036aefff09e2d8452addd32e58e2b9b]
|
||||
reverse link virtualViews to eventtable--><path d="M433.23,167.98 C431.87,193.81 429.73,234.36 428.48,257.89 " fill="none" id="virtualViews<-eventtable" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="433.51,162.7,429.028,171.4699,433.239,167.6926,437.0163,171.9036,433.51,162.7" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[e952d7f61e81b262dbf5ac2ebc7fe5ce]
|
||||
reverse link virtualViews to materialViews--><path d="M471.32,143 C481.33,143 491.34,143 501.36,143 " fill="none" id="virtualViews<-materialViews" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="466.08,143,475.08,147,471.08,143,475.08,139,466.08,143" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[7e09ac5c3e227e26930fb5d40726a924]
|
||||
@startuml
|
||||
|
||||
|
||||
cloud "ecommerce platforms:\n A, B, C, ..." as platforms
|
||||
boundary webhook
|
||||
database db as "db (postgres)" {
|
||||
rectangle "raw events" as eventtable
|
||||
control trigger
|
||||
|
||||
rectangle views {
|
||||
rectangle virtual as virtualViews
|
||||
rectangle material as materialViews
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
platforms -> webhook : event
|
||||
webhook -> eventtable : save
|
||||
eventtable -> trigger : read
|
||||
trigger -up-> materialViews : update
|
||||
eventtable -up-> virtualViews
|
||||
materialViews -left-> virtualViews
|
||||
|
||||
|
||||
@enduml
|
||||
|
||||
PlantUML version 1.2020.02(Sun Mar 01 03:22:07 MST 2020)
|
||||
(GPL source distribution)
|
||||
Java Runtime: OpenJDK Runtime Environment
|
||||
JVM: OpenJDK 64-Bit Server VM
|
||||
Java Version: 21.0.9+10-Ubuntu-124.04
|
||||
Operating System: Linux
|
||||
Default Encoding: UTF-8
|
||||
Language: en
|
||||
Country: US
|
||||
--></g></svg>
|
||||
|
After Width: | Height: | Size: 9.5 KiB |
@@ -0,0 +1,25 @@
|
||||
@startuml
|
||||
|
||||
|
||||
cloud "ecommerce platforms:\n A, B, C, ..." as platforms
|
||||
boundary webhook
|
||||
database db as "db (postgres)" {
|
||||
rectangle "raw events" as eventtable
|
||||
control trigger
|
||||
|
||||
rectangle views {
|
||||
rectangle virtual as virtualViews
|
||||
rectangle material as materialViews
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
platforms -> webhook : event
|
||||
webhook -> eventtable : save
|
||||
eventtable -> trigger : read
|
||||
trigger -up-> materialViews : update
|
||||
eventtable -up-> virtualViews
|
||||
materialViews -left-> virtualViews
|
||||
|
||||
|
||||
@enduml
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,56 @@
|
||||
@startuml
|
||||
|
||||
|
||||
object store_events
|
||||
store_events : platform
|
||||
store_events : store_id
|
||||
store_events : event_id
|
||||
store_events : event_timestamp
|
||||
store_events : tiktok_store_id
|
||||
store_events : tiktok_event_id
|
||||
store_events : <platform B>_store_id
|
||||
store_events : <platform B>_event_id
|
||||
store_events : <platform C>_store_id
|
||||
store_events : <platform C>_event_id
|
||||
store_events : <platform ..>_store_id
|
||||
store_events : <platform ..>_event_id
|
||||
store_events : raw payload?
|
||||
|
||||
note right of store_events::"tiktok_event_id"
|
||||
reference tiktok_store_event_details
|
||||
end note
|
||||
note right of store_events::"<platform B>_event_id"
|
||||
reference <platform B>_store_event_details
|
||||
end note
|
||||
note right of store_events::"<platform C>_event_id"
|
||||
reference <platform C>_store_event_details
|
||||
end note
|
||||
note right of store_events::"<platform ..>_event_id"
|
||||
only one reference can exist at a time
|
||||
(eg event CANNOT be both from tiktok AND etsy)
|
||||
end note
|
||||
|
||||
object tiktok_store_event_details
|
||||
tiktok_store_event_details : store_id
|
||||
tiktok_store_event_details : event_id
|
||||
tiktok_store_event_details : event_timestamp
|
||||
tiktok_store_event_details : [attribute: value ...]
|
||||
|
||||
object "<platform B>_store_event_details" as platform_b_specific_store_event_details
|
||||
platform_b_specific_store_event_details : store_id
|
||||
platform_b_specific_store_event_details : event_id
|
||||
platform_b_specific_store_event_details : event_timestamp
|
||||
platform_b_specific_store_event_details : [attribute: value ...]
|
||||
|
||||
object "<platform C>_store_event_details" as platform_c_specific_store_event_details
|
||||
platform_c_specific_store_event_details : store_id
|
||||
platform_c_specific_store_event_details : event_id
|
||||
platform_c_specific_store_event_details : event_timestamp
|
||||
platform_c_specific_store_event_details : [attribute: value ...]
|
||||
|
||||
store_events <|-- tiktok_store_event_details
|
||||
store_events <|-- platform_b_specific_store_event_details
|
||||
store_events <|-- platform_c_specific_store_event_details
|
||||
|
||||
|
||||
@enduml
|
||||
@@ -0,0 +1,26 @@
|
||||
package site
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func NewSiteHandler() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
w.Write([]byte(`
|
||||
<DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>TEST</title>
|
||||
</head>
|
||||
<body>
|
||||
TEST
|
||||
</body>
|
||||
</html>
|
||||
`))
|
||||
})
|
||||
|
||||
return mux
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package etsy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func NewWebhookHandler() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Write([]byte(fmt.Sprintf("received etsy webhook call: %s %s\n", r.Method, r.URL)))
|
||||
})
|
||||
|
||||
return mux
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package tiktok
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func NewWebhookHandler() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Write([]byte(fmt.Sprintf("received tiktok webhook call: %s %s\n", r.Method, r.URL)))
|
||||
})
|
||||
|
||||
return mux
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package webhooks
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"ruben/inventory2/internal/webhooks/etsy"
|
||||
"ruben/inventory2/internal/webhooks/tiktok"
|
||||
"ruben/inventory2/internal/webhooks/wix"
|
||||
)
|
||||
|
||||
func New() http.Handler {
|
||||
wh := http.NewServeMux()
|
||||
|
||||
wh.Handle("/etsy/", http.StripPrefix("/etsy", etsy.NewWebhookHandler()))
|
||||
wh.Handle("/tiktok/", http.StripPrefix("/tiktok", tiktok.NewWebhookHandler()))
|
||||
wh.Handle("/wix/", http.StripPrefix("/wix", wix.NewWebhookHandler()))
|
||||
|
||||
return wh
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package wix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func NewWebhookHandler() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Write([]byte(fmt.Sprintf("received wix webhook call: %s %s\n", r.Method, r.URL)))
|
||||
})
|
||||
|
||||
return mux
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"ruben/inventory2/internal/site"
|
||||
"ruben/inventory2/internal/webhooks"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := runApp(context.Background()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("application shutdown")
|
||||
}
|
||||
|
||||
func runApp(ctx context.Context) error {
|
||||
ctx, shutdown := context.WithCancel(ctx)
|
||||
defer shutdown()
|
||||
|
||||
// start http server
|
||||
|
||||
srvErrCh := runServer(ctx)
|
||||
|
||||
// wait for interrupt signal or unrecoverable failure, then shutdown
|
||||
|
||||
osSignalCh := make(chan os.Signal, 1)
|
||||
signal.Notify(osSignalCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
var serverAlreadyShutdown bool
|
||||
select {
|
||||
case s := <-osSignalCh:
|
||||
fmt.Println("application received shutdown signal:", s)
|
||||
fmt.Println("shutting down")
|
||||
case err := <-srvErrCh:
|
||||
serverAlreadyShutdown = true
|
||||
fmt.Println("server shutdown unexpectedly")
|
||||
if err != nil {
|
||||
fmt.Println("server encountered error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
shutdown()
|
||||
|
||||
// capture application errors that occurred during or caused shutdown
|
||||
|
||||
var errs []error
|
||||
|
||||
if !serverAlreadyShutdown {
|
||||
if err := <-srvErrCh; err != nil {
|
||||
errs = append(errs, fmt.Errorf("server experienced an error: %w", err))
|
||||
}
|
||||
fmt.Println("server shut down")
|
||||
}
|
||||
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func buildHTTPHandler() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.Handle("/webhooks/", http.StripPrefix("/webhooks", webhooks.New()))
|
||||
mux.Handle("/site/", http.StripPrefix("/site", site.NewSiteHandler()))
|
||||
|
||||
return mux
|
||||
}
|
||||
|
||||
func runServer(ctx context.Context) <-chan error {
|
||||
srv := &http.Server{
|
||||
Addr: ":9000", // local
|
||||
Handler: buildHTTPHandler(),
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
alreadyShutdownCh := make(chan struct{}, 1)
|
||||
|
||||
runningErrCh := make(chan error, 1)
|
||||
go func() {
|
||||
defer close(runningErrCh)
|
||||
defer cancel()
|
||||
defer close(alreadyShutdownCh)
|
||||
|
||||
fmt.Println("server running on 9000...")
|
||||
if err := srv.ListenAndServe(); err != nil {
|
||||
if !errors.Is(err, http.ErrServerClosed) {
|
||||
runningErrCh <- fmt.Errorf("server experienced error: %w", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
shutdownErrCh := make(chan error, 1)
|
||||
go func() {
|
||||
defer close(shutdownErrCh)
|
||||
|
||||
select {
|
||||
case <-alreadyShutdownCh:
|
||||
return
|
||||
case <-ctx.Done():
|
||||
}
|
||||
|
||||
shutdownCtx, cancelShutdown := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancelShutdown()
|
||||
if err := srv.Shutdown(shutdownCtx); err != nil {
|
||||
shutdownErrCh <- fmt.Errorf("error occurred attempting to shutdown server: %w", err)
|
||||
}
|
||||
}()
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
defer close(errCh)
|
||||
|
||||
err1 := <-runningErrCh
|
||||
err2 := <-shutdownErrCh
|
||||
if err := errors.Join(err1, err2); err != nil {
|
||||
errCh <- err
|
||||
}
|
||||
}()
|
||||
|
||||
return errCh
|
||||
}
|
||||
Reference in New Issue
Block a user