diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5af704e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,19 @@ +.* +images +README.md + +# mkdocs +mkdocs.yml +docs +site + +# pipenv +Pipfile +Pipfile.lock + +# amplify console +amplify.yml + +# netlify +netlify.toml +runtime.txt diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..39abb03 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[Makefile] +indent_size = 4 +indent_style = tab diff --git a/.gitignore b/.gitignore index 95ab85f..3342338 100644 --- a/.gitignore +++ b/.gitignore @@ -1,143 +1,8 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* +# mkdocs documentation +/site -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json +# python +.venv -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variable files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next -out - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# vuepress v2.x temp and cache directory -.temp -.cache - -# Docusaurus cache and generated files -.docusaurus - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# yarn v2 -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* - -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -!.vscode/*.code-snippets - -# Local History for Visual Studio Code -.history/ - -# Built Visual Studio Code Extensions -*.vsix +# editor +.vscode diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..ec3731b --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,12 @@ +image: + name: ghcr.io/peaceiris/mkdocs-material:latest + +pages: + script: + - mkdocs build --config-file ./mkdocs-sample.yml + - mv site public + artifacts: + paths: + - public + only: + - main diff --git a/.hadolint.yaml b/.hadolint.yaml new file mode 100644 index 0000000..49c344e --- /dev/null +++ b/.hadolint.yaml @@ -0,0 +1,2 @@ +ignored: + - DL3013 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7df5352 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.10-slim-buster + +LABEL maintainer="*" + +# Install requirements +COPY ./requirements.txt /root +WORKDIR /root +RUN python3 -m pip install --no-cache-dir --upgrade pip && \ + python3 -m pip install --no-cache-dir -r ./requirements.txt && \ + python3 -m pip check + +# Expose MkDocs development server port +EXPOSE 8000 + +# Start development server by default +CMD ["mkdocs", "serve", "--dev-addr=0.0.0.0:8000"] diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..3a3cb38 --- /dev/null +++ b/Pipfile @@ -0,0 +1,30 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[packages] +mkdocs = "*" +mkdocs-material = "*" +python-markdown-math = "*" +Pygments = "*" +prompt_toolkit = "*" +pyyaml = "*" +Jinja2 = "*" +mkdocs-minify-plugin = "*" + +[dev-packages] +invoke = "*" + +[requires] +python_version = "*" + +[pipenv] +allow_prereleases = true + +[scripts] +version = "mkdocs --version" +help = "mkdocs --help" +serve = "inv serve" +build = "mkdocs build" +deploy = "mkdocs gh-deploy" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..75674db --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,338 @@ +{ + "_meta": { + "hash": { + "sha256": "483be911ddead5be5701044147e4bbf81c448d49c619338a1de225143a494fd8" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "*" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "click": { + "hashes": [ + "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", + "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" + ], + "markers": "python_version >= '3.7'", + "version": "==8.1.3" + }, + "colorama": { + "hashes": [ + "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da", + "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4" + ], + "markers": "platform_system == 'Windows'", + "version": "==0.4.5" + }, + "csscompressor": { + "hashes": [ + "sha256:afa22badbcf3120a4f392e4d22f9fff485c044a1feda4a950ecc5eba9dd31a05" + ], + "version": "==0.9.5" + }, + "ghp-import": { + "hashes": [ + "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", + "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343" + ], + "version": "==2.1.0" + }, + "htmlmin": { + "hashes": [ + "sha256:50c1ef4630374a5d723900096a961cff426dff46b48f34d194a81bbe14eca178" + ], + "version": "==0.1.12" + }, + "importlib-metadata": { + "hashes": [ + "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670", + "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23" + ], + "markers": "python_version >= '3.7'", + "version": "==4.12.0" + }, + "jinja2": { + "hashes": [ + "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", + "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" + ], + "index": "pypi", + "version": "==3.1.2" + }, + "jsmin": { + "hashes": [ + "sha256:c0959a121ef94542e807a674142606f7e90214a2b3d1eb17300244bbb5cc2bfc" + ], + "version": "==3.0.1" + }, + "markdown": { + "hashes": [ + "sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874", + "sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621" + ], + "markers": "python_version >= '3.6'", + "version": "==3.3.7" + }, + "markupsafe": { + "hashes": [ + "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003", + "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88", + "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5", + "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7", + "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a", + "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603", + "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1", + "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135", + "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247", + "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6", + "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601", + "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77", + "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02", + "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e", + "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63", + "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f", + "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980", + "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b", + "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812", + "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff", + "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96", + "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1", + "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925", + "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a", + "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6", + "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e", + "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f", + "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4", + "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f", + "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3", + "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c", + "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a", + "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417", + "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a", + "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a", + "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37", + "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452", + "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933", + "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a", + "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7" + ], + "markers": "python_version >= '3.7'", + "version": "==2.1.1" + }, + "mergedeep": { + "hashes": [ + "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", + "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307" + ], + "markers": "python_version >= '3.6'", + "version": "==1.3.4" + }, + "mkdocs": { + "hashes": [ + "sha256:26bd2b03d739ac57a3e6eed0b7bcc86168703b719c27b99ad6ca91dc439aacde", + "sha256:b504405b04da38795fec9b2e5e28f6aa3a73bb0960cb6d5d27ead28952bd35ea" + ], + "index": "pypi", + "version": "==1.3.0" + }, + "mkdocs-material": { + "hashes": [ + "sha256:263f2721f3abe533b61f7c8bed435a0462620912742c919821ac2d698b4bfe67", + "sha256:dc82b667d2a83f0de581b46a6d0949732ab77e7638b87ea35b770b33bc02e75a" + ], + "index": "pypi", + "version": "==8.3.9" + }, + "mkdocs-material-extensions": { + "hashes": [ + "sha256:a82b70e533ce060b2a5d9eb2bc2e1be201cf61f901f93704b4acf6e3d5983a44", + "sha256:bfd24dfdef7b41c312ede42648f9eb83476ea168ec163b613f9abd12bbfddba2" + ], + "markers": "python_version >= '3.6'", + "version": "==1.0.3" + }, + "mkdocs-minify-plugin": { + "hashes": [ + "sha256:32d9e8fbd89327a0f4f648f517297aad344c1bad64cfde110d059bd2f2780a6d", + "sha256:487c31ae6b8b3230f56910ce6bcf5c7e6ad9a8c4f51c720a4b989f30c2b0233f" + ], + "index": "pypi", + "version": "==0.5.0" + }, + "packaging": { + "hashes": [ + "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", + "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" + ], + "markers": "python_version >= '3.6'", + "version": "==21.3" + }, + "prompt-toolkit": { + "hashes": [ + "sha256:859b283c50bde45f5f97829f77a4674d1c1fcd88539364f1b28a37805cfd89c0", + "sha256:d8916d3f62a7b67ab353a952ce4ced6a1d2587dfe9ef8ebc30dd7c386751f289" + ], + "index": "pypi", + "version": "==3.0.30" + }, + "pygments": { + "hashes": [ + "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb", + "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519" + ], + "index": "pypi", + "version": "==2.12.0" + }, + "pymdown-extensions": { + "hashes": [ + "sha256:3ef2d998c0d5fa7eb09291926d90d69391283561cf6306f85cd588a5eb5befa0", + "sha256:ec141c0f4983755349f0c8710416348d1a13753976c028186ed14f190c8061c4" + ], + "markers": "python_version >= '3.7'", + "version": "==9.5" + }, + "pyparsing": { + "hashes": [ + "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", + "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" + ], + "markers": "python_full_version >= '3.6.8'", + "version": "==3.0.9" + }, + "python-dateutil": { + "hashes": [ + "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", + "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.8.2" + }, + "python-markdown-math": { + "hashes": [ + "sha256:8564212af679fc18d53f38681f16080fcd3d186073f23825c7ce86fadd3e3635", + "sha256:c685249d84b5b697e9114d7beb352bd8ca2e07fd268fd4057ffca888c14641e5" + ], + "index": "pypi", + "version": "==0.8" + }, + "pyyaml": { + "hashes": [ + "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", + "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", + "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", + "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", + "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", + "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", + "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", + "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", + "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", + "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", + "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", + "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", + "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", + "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", + "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", + "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", + "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", + "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", + "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", + "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", + "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", + "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", + "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", + "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", + "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", + "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", + "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", + "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", + "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", + "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", + "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", + "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", + "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" + ], + "index": "pypi", + "version": "==6.0" + }, + "pyyaml-env-tag": { + "hashes": [ + "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb", + "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069" + ], + "markers": "python_version >= '3.6'", + "version": "==0.1" + }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" + }, + "watchdog": { + "hashes": [ + "sha256:083171652584e1b8829581f965b9b7723ca5f9a2cd7e20271edf264cfd7c1412", + "sha256:117ffc6ec261639a0209a3252546b12800670d4bf5f84fbd355957a0595fe654", + "sha256:186f6c55abc5e03872ae14c2f294a153ec7292f807af99f57611acc8caa75306", + "sha256:195fc70c6e41237362ba720e9aaf394f8178bfc7fa68207f112d108edef1af33", + "sha256:226b3c6c468ce72051a4c15a4cc2ef317c32590d82ba0b330403cafd98a62cfd", + "sha256:247dcf1df956daa24828bfea5a138d0e7a7c98b1a47cf1fa5b0c3c16241fcbb7", + "sha256:255bb5758f7e89b1a13c05a5bceccec2219f8995a3a4c4d6968fe1de6a3b2892", + "sha256:43ce20ebb36a51f21fa376f76d1d4692452b2527ccd601950d69ed36b9e21609", + "sha256:4f4e1c4aa54fb86316a62a87b3378c025e228178d55481d30d857c6c438897d6", + "sha256:5952135968519e2447a01875a6f5fc8c03190b24d14ee52b0f4b1682259520b1", + "sha256:64a27aed691408a6abd83394b38503e8176f69031ca25d64131d8d640a307591", + "sha256:6b17d302850c8d412784d9246cfe8d7e3af6bcd45f958abb2d08a6f8bedf695d", + "sha256:70af927aa1613ded6a68089a9262a009fbdf819f46d09c1a908d4b36e1ba2b2d", + "sha256:7a833211f49143c3d336729b0020ffd1274078e94b0ae42e22f596999f50279c", + "sha256:8250546a98388cbc00c3ee3cc5cf96799b5a595270dfcfa855491a64b86ef8c3", + "sha256:97f9752208f5154e9e7b76acc8c4f5a58801b338de2af14e7e181ee3b28a5d39", + "sha256:9f05a5f7c12452f6a27203f76779ae3f46fa30f1dd833037ea8cbc2887c60213", + "sha256:a735a990a1095f75ca4f36ea2ef2752c99e6ee997c46b0de507ba40a09bf7330", + "sha256:ad576a565260d8f99d97f2e64b0f97a48228317095908568a9d5c786c829d428", + "sha256:b530ae007a5f5d50b7fbba96634c7ee21abec70dc3e7f0233339c81943848dc1", + "sha256:bfc4d351e6348d6ec51df007432e6fe80adb53fd41183716017026af03427846", + "sha256:d3dda00aca282b26194bdd0adec21e4c21e916956d972369359ba63ade616153", + "sha256:d9820fe47c20c13e3c9dd544d3706a2a26c02b2b43c993b62fcd8011bcc0adb3", + "sha256:ed80a1628cee19f5cfc6bb74e173f1b4189eb532e705e2a13e3250312a62e0c9", + "sha256:ee3e38a6cc050a8830089f79cbec8a3878ec2fe5160cdb2dc8ccb6def8552658" + ], + "markers": "python_version >= '3.6'", + "version": "==2.1.9" + }, + "wcwidth": { + "hashes": [ + "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784", + "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83" + ], + "version": "==0.2.5" + }, + "zipp": { + "hashes": [ + "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad", + "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099" + ], + "markers": "python_version >= '3.7'", + "version": "==3.8.0" + } + }, + "develop": { + "invoke": { + "hashes": [ + "sha256:2dc975b4f92be0c0a174ad2d063010c8a1fdb5e9389d69871001118b4fcac4fb", + "sha256:7b6deaf585eee0a848205d0b8c0014b9bf6f287a8eb798818a642dff1df14b19" + ], + "index": "pypi", + "version": "==1.7.1" + } + } +} diff --git a/amplify.yml b/amplify.yml new file mode 100644 index 0000000..65ca9f6 --- /dev/null +++ b/amplify.yml @@ -0,0 +1,18 @@ +version: 1.0 +frontend: + phases: + # IMPORTANT - Please verify your build commands + preBuild: + commands: + - python3 -m pip install --upgrade pip + - python3 -m pip install -r requirements.txt + build: + commands: + - mkdocs build + artifacts: + # IMPORTANT - Please verify your build output directory + baseDirectory: site + files: + - '**/*' + cache: + paths: [] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..c1d002b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,18 @@ +version: '3' + +services: + mkdocs: + image: ghcr.io/peaceiris/mkdocs-material:latest + container_name: Stack_Army + ports: + - 8000:8000 + volumes: + - ${PWD}:/root + stdin_open: true + tty: true + command: + - "mkdocs" + - "serve" + - "--dev-addr=0.0.0.0:8000" + - "--config-file" + - "./mkdocs.yml" diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 0000000..a26d897 --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +stackarmy.taraldinn.me \ No newline at end of file diff --git a/docs/ClassLink.md b/docs/ClassLink.md new file mode 100644 index 0000000..5e9b5d5 --- /dev/null +++ b/docs/ClassLink.md @@ -0,0 +1,35 @@ +# Live Class +## Live Class Recording + +**Table of Contents:** + +- [0. Full-stack Army - Welcome | Decision-Making Video](https://www.youtube.com/watch?v=ewBBT6Iph0M&t=784s) +- [Lecture 1 - Application Requirements & Landscape | Development Big Picture](https://youtu.be/AnD2KuKNsxE) +- [Lecture 2 - We Need Freedom, We have to Stop Technology War](https://youtu.be/9ltapy7kK5w) +- [Lecture 3 - Programming Language Foundation - A Bigger Landscape](https://youtu.be/1LWq-OdB7jY) +- [Lecture 4 - Programming Fundamentals using JavaScript](https://youtu.be/tAkUXTvm-xo) +- [Lecture 5 - Array Operations - Imperative vs Declarative](https://youtu.be/LADJO7KniNY) +- [Lecture 6 - JavaScript Array and Object Deep Dive](https://youtu.be/Mr5rksCjybA) +- [Lecture 7 - QNA 1 - Don't Miss The Last Part](https://youtu.be/TAa7gSbPVis) +- [Lecture 8 - Understand JavaScript Functions | Function as a value](https://youtu.be/lctjTl1ftdw) +- [Lecture 9 - Functional Programming in JavaScript](https://youtu.be/wMy2IZ12mxM) +- [Lecture 10 - Asynchronous Programming in JavaScript](https://youtu.be/OCkxS7W3gwU) +- [Lecture 11 - Async Iterator & Generator in JavaScript | Project Requirements](https://youtu.be/phzeyHwoIrQ) +- [Lecture 12 - Attendance System Requirement Analysis](https://youtu.be/Gsj7uU_7Um4) +- [Lecture 13 - Create Models, Write Pseudo Code and Adda](https://youtu.be/BfGOYh9Fdwg) +- [Lecture 14 - Backend 1 | Course planning and discussion](https://youtu.be/QBTOAGGgehA) +- [Lecture 15 - [Backend 2] Introduction to Backend Development](https://youtu.be/Mc6UEF957hU) +- [Lecture 16 - [Backend 3] Understand Express Middleware](https://youtu.be/kXeNJJ4mQ7w) +- [Lecture 17 - [Backend 4] Raffle Draw Project](https://youtu.be/4D2DIu8bhqU) +- [Lecture 18 - [Backend 5] Understand The Concepts of Database](https://youtu.be/SyKO3oZLz00) +- [Lecture 19 - [Backend 6] Adda with Random Topics | You can Skip](https://youtu.be/mqq5VgRMIho) +- [Lecture 20 - [Backend 7] Start Working with Mongoose](https://youtu.be/y5Rism0fEqE) +- [Lecture 21 - QNA on Express 101 and Books](https://youtu.be/krI6QUCGHY4) +- [Lecture 22 - Authentication System from Pseudo Code to Real Code](https://youtu.be/0gl4grplEcI) +- [Lecture 23 - Implement JWT and Refactor The Project Structure](https://youtu.be/D5A5BSGQVBU) +- [Lecture 24 - Implement User CRUD Operations](https://youtu.be/xr0sKPvAipQ) +- [Lecture 25 - QNA on 5 Recorded Courses and Motivational ADDA](https://youtu.be/KseSdSmvvuM) +- [Lecture 26 - Implement Attendance System Main Functionalities](https://youtu.be/A5S7mWxqs2s) +- [Lecture 27 - Frontend Core Concepts and Communication](https://youtu.be/0T7YagglhFY) +- [Lecture 28 [Frontend 1] - Frontend Course Planning & Discussion](https://youtu.be/FppAFtsxICk) +- [Lecture 29 [Frontend 2] - Understand React in A Different Way](https://youtu.be/vmw-sSTFwAk) diff --git a/class-overview/Lecture-22/README.MD b/docs/Lectures/Authenticaion/22/Overview.MD similarity index 100% rename from class-overview/Lecture-22/README.MD rename to docs/Lectures/Authenticaion/22/Overview.MD diff --git a/class-overview/Lecture-22/images/console.png b/docs/Lectures/Authenticaion/22/images/console.png similarity index 100% rename from class-overview/Lecture-22/images/console.png rename to docs/Lectures/Authenticaion/22/images/console.png diff --git a/class-overview/Lecture-22/images/db-1.png b/docs/Lectures/Authenticaion/22/images/db-1.png similarity index 100% rename from class-overview/Lecture-22/images/db-1.png rename to docs/Lectures/Authenticaion/22/images/db-1.png diff --git a/class-overview/Lecture-22/images/db-2.png b/docs/Lectures/Authenticaion/22/images/db-2.png similarity index 100% rename from class-overview/Lecture-22/images/db-2.png rename to docs/Lectures/Authenticaion/22/images/db-2.png diff --git a/class-overview/Lecture-22/images/login-1.png b/docs/Lectures/Authenticaion/22/images/login-1.png similarity index 100% rename from class-overview/Lecture-22/images/login-1.png rename to docs/Lectures/Authenticaion/22/images/login-1.png diff --git a/class-overview/Lecture-22/images/login-2.png b/docs/Lectures/Authenticaion/22/images/login-2.png similarity index 100% rename from class-overview/Lecture-22/images/login-2.png rename to docs/Lectures/Authenticaion/22/images/login-2.png diff --git a/class-overview/Lecture-12/login-process.jpg b/docs/Lectures/Authenticaion/22/images/login-process.jpg similarity index 100% rename from class-overview/Lecture-12/login-process.jpg rename to docs/Lectures/Authenticaion/22/images/login-process.jpg diff --git a/class-overview/Lecture-22/images/reg-2.png b/docs/Lectures/Authenticaion/22/images/reg-2.png similarity index 100% rename from class-overview/Lecture-22/images/reg-2.png rename to docs/Lectures/Authenticaion/22/images/reg-2.png diff --git a/class-overview/Lecture-22/images/reg-3.png b/docs/Lectures/Authenticaion/22/images/reg-3.png similarity index 100% rename from class-overview/Lecture-22/images/reg-3.png rename to docs/Lectures/Authenticaion/22/images/reg-3.png diff --git a/class-overview/Lecture-22/images/reg-4.png b/docs/Lectures/Authenticaion/22/images/reg-4.png similarity index 100% rename from class-overview/Lecture-22/images/reg-4.png rename to docs/Lectures/Authenticaion/22/images/reg-4.png diff --git a/class-overview/Lecture-22/images/reg-5.png b/docs/Lectures/Authenticaion/22/images/reg-5.png similarity index 100% rename from class-overview/Lecture-22/images/reg-5.png rename to docs/Lectures/Authenticaion/22/images/reg-5.png diff --git a/class-overview/Lecture-22/images/reg-6.png b/docs/Lectures/Authenticaion/22/images/reg-6.png similarity index 100% rename from class-overview/Lecture-22/images/reg-6.png rename to docs/Lectures/Authenticaion/22/images/reg-6.png diff --git a/class-overview/Lecture-22/images/register1.png b/docs/Lectures/Authenticaion/22/images/register1.png similarity index 100% rename from class-overview/Lecture-22/images/register1.png rename to docs/Lectures/Authenticaion/22/images/register1.png diff --git a/class-overview/Lecture-12/registration-process.jpg b/docs/Lectures/Authenticaion/22/images/registration-process.jpg similarity index 100% rename from class-overview/Lecture-12/registration-process.jpg rename to docs/Lectures/Authenticaion/22/images/registration-process.jpg diff --git a/resources/lecture-22/README.md b/docs/Lectures/Authenticaion/22/resource.md similarity index 100% rename from resources/lecture-22/README.md rename to docs/Lectures/Authenticaion/22/resource.md diff --git a/class-overview/Lecture-23/README.md b/docs/Lectures/Authenticaion/23/Overview.md similarity index 100% rename from class-overview/Lecture-23/README.md rename to docs/Lectures/Authenticaion/23/Overview.md diff --git a/class-overview/Lecture-23/images/auth.png b/docs/Lectures/Authenticaion/23/images/auth.png similarity index 100% rename from class-overview/Lecture-23/images/auth.png rename to docs/Lectures/Authenticaion/23/images/auth.png diff --git a/class-overview/Lecture-23/images/bearer.png b/docs/Lectures/Authenticaion/23/images/bearer.png similarity index 100% rename from class-overview/Lecture-23/images/bearer.png rename to docs/Lectures/Authenticaion/23/images/bearer.png diff --git a/class-overview/Lecture-23/images/date.png b/docs/Lectures/Authenticaion/23/images/date.png similarity index 100% rename from class-overview/Lecture-23/images/date.png rename to docs/Lectures/Authenticaion/23/images/date.png diff --git a/class-overview/Lecture-23/images/decode-1.png b/docs/Lectures/Authenticaion/23/images/decode-1.png similarity index 100% rename from class-overview/Lecture-23/images/decode-1.png rename to docs/Lectures/Authenticaion/23/images/decode-1.png diff --git a/class-overview/Lecture-23/images/decode.png b/docs/Lectures/Authenticaion/23/images/decode.png similarity index 100% rename from class-overview/Lecture-23/images/decode.png rename to docs/Lectures/Authenticaion/23/images/decode.png diff --git a/class-overview/Lecture-23/images/header.png b/docs/Lectures/Authenticaion/23/images/header.png similarity index 100% rename from class-overview/Lecture-23/images/header.png rename to docs/Lectures/Authenticaion/23/images/header.png diff --git a/class-overview/Lecture-23/images/new-db.png b/docs/Lectures/Authenticaion/23/images/new-db.png similarity index 100% rename from class-overview/Lecture-23/images/new-db.png rename to docs/Lectures/Authenticaion/23/images/new-db.png diff --git a/class-overview/Lecture-23/images/new-login.png b/docs/Lectures/Authenticaion/23/images/new-login.png similarity index 100% rename from class-overview/Lecture-23/images/new-login.png rename to docs/Lectures/Authenticaion/23/images/new-login.png diff --git a/class-overview/Lecture-23/images/new-reg.png b/docs/Lectures/Authenticaion/23/images/new-reg.png similarity index 100% rename from class-overview/Lecture-23/images/new-reg.png rename to docs/Lectures/Authenticaion/23/images/new-reg.png diff --git a/class-overview/Lecture-23/images/pvt.png b/docs/Lectures/Authenticaion/23/images/pvt.png similarity index 100% rename from class-overview/Lecture-23/images/pvt.png rename to docs/Lectures/Authenticaion/23/images/pvt.png diff --git a/class-overview/Lecture-23/images/tkn-1.png b/docs/Lectures/Authenticaion/23/images/tkn-1.png similarity index 100% rename from class-overview/Lecture-23/images/tkn-1.png rename to docs/Lectures/Authenticaion/23/images/tkn-1.png diff --git a/class-overview/Lecture-23/images/tkn-2.png b/docs/Lectures/Authenticaion/23/images/tkn-2.png similarity index 100% rename from class-overview/Lecture-23/images/tkn-2.png rename to docs/Lectures/Authenticaion/23/images/tkn-2.png diff --git a/class-overview/Lecture-23/images/token-1.png b/docs/Lectures/Authenticaion/23/images/token-1.png similarity index 100% rename from class-overview/Lecture-23/images/token-1.png rename to docs/Lectures/Authenticaion/23/images/token-1.png diff --git a/class-overview/Lecture-23/images/v1.png b/docs/Lectures/Authenticaion/23/images/v1.png similarity index 100% rename from class-overview/Lecture-23/images/v1.png rename to docs/Lectures/Authenticaion/23/images/v1.png diff --git a/class-overview/Lecture-24/README.md b/docs/Lectures/Authenticaion/24/Overview.md similarity index 100% rename from class-overview/Lecture-24/README.md rename to docs/Lectures/Authenticaion/24/Overview.md diff --git a/class-overview/Lecture-24/images/del.png b/docs/Lectures/Authenticaion/24/images/del.png similarity index 100% rename from class-overview/Lecture-24/images/del.png rename to docs/Lectures/Authenticaion/24/images/del.png diff --git a/class-overview/Lecture-24/images/get-users.png b/docs/Lectures/Authenticaion/24/images/get-users.png similarity index 100% rename from class-overview/Lecture-24/images/get-users.png rename to docs/Lectures/Authenticaion/24/images/get-users.png diff --git a/class-overview/Lecture-24/images/getid.png b/docs/Lectures/Authenticaion/24/images/getid.png similarity index 100% rename from class-overview/Lecture-24/images/getid.png rename to docs/Lectures/Authenticaion/24/images/getid.png diff --git a/class-overview/Lecture-24/images/getiderr.png b/docs/Lectures/Authenticaion/24/images/getiderr.png similarity index 100% rename from class-overview/Lecture-24/images/getiderr.png rename to docs/Lectures/Authenticaion/24/images/getiderr.png diff --git a/class-overview/Lecture-24/images/login-1.png b/docs/Lectures/Authenticaion/24/images/login-1.png similarity index 100% rename from class-overview/Lecture-24/images/login-1.png rename to docs/Lectures/Authenticaion/24/images/login-1.png diff --git a/class-overview/Lecture-24/images/new-user.png b/docs/Lectures/Authenticaion/24/images/new-user.png similarity index 100% rename from class-overview/Lecture-24/images/new-user.png rename to docs/Lectures/Authenticaion/24/images/new-user.png diff --git a/class-overview/Lecture-24/images/patch.png b/docs/Lectures/Authenticaion/24/images/patch.png similarity index 100% rename from class-overview/Lecture-24/images/patch.png rename to docs/Lectures/Authenticaion/24/images/patch.png diff --git a/class-overview/Lecture-24/images/put.png b/docs/Lectures/Authenticaion/24/images/put.png similarity index 100% rename from class-overview/Lecture-24/images/put.png rename to docs/Lectures/Authenticaion/24/images/put.png diff --git a/class-overview/Lecture-24/images/reg.png b/docs/Lectures/Authenticaion/24/images/reg.png similarity index 100% rename from class-overview/Lecture-24/images/reg.png rename to docs/Lectures/Authenticaion/24/images/reg.png diff --git a/class-overview/Lecture-24/images/unauth.png b/docs/Lectures/Authenticaion/24/images/unauth.png similarity index 100% rename from class-overview/Lecture-24/images/unauth.png rename to docs/Lectures/Authenticaion/24/images/unauth.png diff --git a/class-overview/Lecture-24/images/user-crud.png b/docs/Lectures/Authenticaion/24/images/user-crud.png similarity index 100% rename from class-overview/Lecture-24/images/user-crud.png rename to docs/Lectures/Authenticaion/24/images/user-crud.png diff --git a/class-overview/Lecture-24/images/user-token.png b/docs/Lectures/Authenticaion/24/images/user-token.png similarity index 100% rename from class-overview/Lecture-24/images/user-token.png rename to docs/Lectures/Authenticaion/24/images/user-token.png diff --git a/class-overview/Lecture-25/README.md b/docs/Lectures/Authenticaion/25/Overview.md similarity index 100% rename from class-overview/Lecture-25/README.md rename to docs/Lectures/Authenticaion/25/Overview.md diff --git a/class-overview/Lecture-26/README.md b/docs/Lectures/Authenticaion/26/Overview.md similarity index 100% rename from class-overview/Lecture-26/README.md rename to docs/Lectures/Authenticaion/26/Overview.md diff --git a/class-overview/Lecture-26/images/attend-2.png b/docs/Lectures/Authenticaion/26/images/attend-2.png similarity index 100% rename from class-overview/Lecture-26/images/attend-2.png rename to docs/Lectures/Authenticaion/26/images/attend-2.png diff --git a/class-overview/Lecture-26/images/attend-fail.png b/docs/Lectures/Authenticaion/26/images/attend-fail.png similarity index 100% rename from class-overview/Lecture-26/images/attend-fail.png rename to docs/Lectures/Authenticaion/26/images/attend-fail.png diff --git a/class-overview/Lecture-26/images/attend.png b/docs/Lectures/Authenticaion/26/images/attend.png similarity index 100% rename from class-overview/Lecture-26/images/attend.png rename to docs/Lectures/Authenticaion/26/images/attend.png diff --git a/class-overview/Lecture-26/images/disable.png b/docs/Lectures/Authenticaion/26/images/disable.png similarity index 100% rename from class-overview/Lecture-26/images/disable.png rename to docs/Lectures/Authenticaion/26/images/disable.png diff --git a/class-overview/Lecture-26/images/get-enable.png b/docs/Lectures/Authenticaion/26/images/get-enable.png similarity index 100% rename from class-overview/Lecture-26/images/get-enable.png rename to docs/Lectures/Authenticaion/26/images/get-enable.png diff --git a/class-overview/Lecture-26/images/notion-2.png b/docs/Lectures/Authenticaion/26/images/notion-2.png similarity index 100% rename from class-overview/Lecture-26/images/notion-2.png rename to docs/Lectures/Authenticaion/26/images/notion-2.png diff --git a/class-overview/Lecture-26/images/notion.png b/docs/Lectures/Authenticaion/26/images/notion.png similarity index 100% rename from class-overview/Lecture-26/images/notion.png rename to docs/Lectures/Authenticaion/26/images/notion.png diff --git a/class-overview/Lecture-26/images/running-1.png b/docs/Lectures/Authenticaion/26/images/running-1.png similarity index 100% rename from class-overview/Lecture-26/images/running-1.png rename to docs/Lectures/Authenticaion/26/images/running-1.png diff --git a/class-overview/Lecture-26/images/running.png b/docs/Lectures/Authenticaion/26/images/running.png similarity index 100% rename from class-overview/Lecture-26/images/running.png rename to docs/Lectures/Authenticaion/26/images/running.png diff --git a/class-overview/Lecture-26/images/st-status.png b/docs/Lectures/Authenticaion/26/images/st-status.png similarity index 100% rename from class-overview/Lecture-26/images/st-status.png rename to docs/Lectures/Authenticaion/26/images/st-status.png diff --git a/class-overview/Lecture-26/images/status-com.png b/docs/Lectures/Authenticaion/26/images/status-com.png similarity index 100% rename from class-overview/Lecture-26/images/status-com.png rename to docs/Lectures/Authenticaion/26/images/status-com.png diff --git a/class-overview/Lecture-26/images/status.png b/docs/Lectures/Authenticaion/26/images/status.png similarity index 100% rename from class-overview/Lecture-26/images/status.png rename to docs/Lectures/Authenticaion/26/images/status.png diff --git a/docs/Lectures/Backend/14/Overview.md b/docs/Lectures/Backend/14/Overview.md new file mode 100644 index 0000000..b62f842 --- /dev/null +++ b/docs/Lectures/Backend/14/Overview.md @@ -0,0 +1,32 @@ +## Lecture 14 - Backend 1 | Course planning and discussion + +আজ থেকে পূর্বের কিছু ব্যাকএন্ডের ক্লাস নিয়ে আলোচনা হবে। এখানে এক্সপ্রেস নিয়ে বিস্তারিত আলোচনা করা হয়েছিল। সেভাবেই ক্লাসের ওভারভিউ তৈরি হবে। অনেক কিছু আগে আলোচনা হয়েছে এমন টপিকও থাকতে পারে। ক্লাসে যা যা আলোচনা হবে তা যদি আগের ক্লাসেও থেকে থাকে তবুও এখানে আমি লিখবো। শুরু করা যাক। + +ব্যাকএন্ডের মাস্টার লেভেল বলতে কিছু হয় না। যতোই যেতে থাকবেন ততোই মনে হবে শেখা কম পড়ে গেছে। যারা ব্যাকএন্ড ডেভেলপার তারা প্রব্লেম সলভ করতে ভালবাসেন। ব্যাকএন্ডের জন্য প্রব্লেম সলভিং স্কিল অনেক গুরত্বপূর্ণ। কারণ একটা অ্যাপ্লিকেশনের ব্যাকএন্ড অন্য অ্যাপ্লিকেশনে ব্যবহার করা যায় না সাধারণত। ফ্রন্টএন্ডের একটা মজা আছে। তা হলো আমি একটা অ্যাপ্লিকেশনের জন্য যদি একটা কম্পোনেন্ট ডিজাইন করি তাহলে অন্য অ্যাপ্লিকেশনেও আমরা সেই কম্পোনেন্ট ইউজ করতে পারবো। কিন্তু ব্যাকএন্ডের বিজনেস লজিক কখনও এক হয় না। কিছু কিছু মডিউলস ইউজ করা যায়। যেমন লগইন, অথেনটিকেশন। কিন্তু ম্যাক্সিমাম ক্ষেত্রে কিছু চেইঞ্জ থাকবেই। + +আজকের ক্লাসে ব্যাকএন্ড জার্নির একটা কারিকুলাম দেয়া হয়েছে। সেটা নিচে দেয়া হলো। + +- ExpressJS +- MongoDB +- PostgreSQL +- REST API +- GraphQL +- Application Building Process + - Architecture + - Cloud + - Documentation + - Testing + - Unit Testing + - Acceptance Testing + - Caching + - Email and SMS + - Event Driven Development + - Distributed Login +- Serverless + - No code low code + +এগুলোই মূলত এই ক্লাসের আলোচ্য বিষয় ছিল। + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/resources/lecture-14/README.md b/docs/Lectures/Backend/14/resource.md similarity index 100% rename from resources/lecture-14/README.md rename to docs/Lectures/Backend/14/resource.md diff --git a/class-overview/Lecture-15/README.md b/docs/Lectures/Backend/15/Overview.md similarity index 100% rename from class-overview/Lecture-15/README.md rename to docs/Lectures/Backend/15/Overview.md diff --git a/class-overview/Lecture-15/images/1000.png b/docs/Lectures/Backend/15/images/1000.png similarity index 100% rename from class-overview/Lecture-15/images/1000.png rename to docs/Lectures/Backend/15/images/1000.png diff --git a/class-overview/Lecture-15/images/500.png b/docs/Lectures/Backend/15/images/500.png similarity index 100% rename from class-overview/Lecture-15/images/500.png rename to docs/Lectures/Backend/15/images/500.png diff --git a/class-overview/Lecture-15/images/all.png b/docs/Lectures/Backend/15/images/all.png similarity index 100% rename from class-overview/Lecture-15/images/all.png rename to docs/Lectures/Backend/15/images/all.png diff --git a/class-overview/Lecture-15/images/books.png b/docs/Lectures/Backend/15/images/books.png similarity index 100% rename from class-overview/Lecture-15/images/books.png rename to docs/Lectures/Backend/15/images/books.png diff --git a/class-overview/Lecture-15/images/postreq.png b/docs/Lectures/Backend/15/images/postreq.png similarity index 100% rename from class-overview/Lecture-15/images/postreq.png rename to docs/Lectures/Backend/15/images/postreq.png diff --git a/class-overview/Lecture-15/images/postres.png b/docs/Lectures/Backend/15/images/postres.png similarity index 100% rename from class-overview/Lecture-15/images/postres.png rename to docs/Lectures/Backend/15/images/postres.png diff --git a/resources/lecture-15/README.md b/docs/Lectures/Backend/15/resource.md similarity index 100% rename from resources/lecture-15/README.md rename to docs/Lectures/Backend/15/resource.md diff --git a/class-overview/Lecture-16/README.md b/docs/Lectures/Backend/16/Overview.md similarity index 100% rename from class-overview/Lecture-16/README.md rename to docs/Lectures/Backend/16/Overview.md diff --git a/class-overview/Lecture-16/images/error.png b/docs/Lectures/Backend/16/images/error.png similarity index 100% rename from class-overview/Lecture-16/images/error.png rename to docs/Lectures/Backend/16/images/error.png diff --git a/docs/Lectures/Backend/16/resource.md b/docs/Lectures/Backend/16/resource.md new file mode 100644 index 0000000..999f520 --- /dev/null +++ b/docs/Lectures/Backend/16/resource.md @@ -0,0 +1,70 @@ +# Lecture 16 - [Backend 3] Understand Express Middleware + +- Middleware +- Project Structure +- Project + +## Middleware + +- What is Middleware? + + - Middleware is just a controller function. It comes from DRY (Don't repeat yourself) principle. It actually used to reuse the controller. + Req -> M1 -> M2 -> M3 -> Res + + ```js + // We will never call it, express will automatically invoke for us. + // This is middleware + function xyz(req, res, next) { + next(); + } + + // This is controller + function xyz(req, res, next) { + res.send(); + } + ``` + + The basic difference between a middleware and controller is, middleware calls `next()` function and a controller calls function related to `response`. + + ```js + // Demo Code + const express = require('express'); + + const app = express(); + + app.use(express.static(__dirname + '/public')); + + const simpleLogger = (req, res, next) => { + console.log(`${req.url} - ${req.method} - ${new Date().toISOString()}`); + next(); + }; + + const secondMiddleWare = (res, req, next) => { + console.log('I am second middleware'); + next(); + }; + + app.use([simpleLogger, secondMiddleWare]); + + app.get('/hello', (req, res, next) => { + res.json({ message: 'Hello' }); + }); + + app.get('/', (req, res, next) => { + res.json({ message: 'Sweet Home' }); + }); + + app.listen(8000, () => { + console.log('Application running on port 8000'); + }); + ``` + +### References + +- [Source Code](../../src/lecture-16/) +- [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) +- [Class Overview](../../Class%20Overview/Lecture-16/README.md) + +#### Task + +- Watch the video and create the structure as shown in video. Write and article with explanation of the structure. diff --git a/class-overview/Lecture-17/README.md b/docs/Lectures/Backend/17/Overview.md similarity index 100% rename from class-overview/Lecture-17/README.md rename to docs/Lectures/Backend/17/Overview.md diff --git a/class-overview/Lecture-17/images/bulk.png b/docs/Lectures/Backend/17/images/bulk.png similarity index 100% rename from class-overview/Lecture-17/images/bulk.png rename to docs/Lectures/Backend/17/images/bulk.png diff --git a/class-overview/Lecture-17/images/delete.png b/docs/Lectures/Backend/17/images/delete.png similarity index 100% rename from class-overview/Lecture-17/images/delete.png rename to docs/Lectures/Backend/17/images/delete.png diff --git a/class-overview/Lecture-17/images/draw-1.png b/docs/Lectures/Backend/17/images/draw-1.png similarity index 100% rename from class-overview/Lecture-17/images/draw-1.png rename to docs/Lectures/Backend/17/images/draw-1.png diff --git a/class-overview/Lecture-17/images/draw-2.png b/docs/Lectures/Backend/17/images/draw-2.png similarity index 100% rename from class-overview/Lecture-17/images/draw-2.png rename to docs/Lectures/Backend/17/images/draw-2.png diff --git a/class-overview/Lecture-17/images/findbyid.png b/docs/Lectures/Backend/17/images/findbyid.png similarity index 100% rename from class-overview/Lecture-17/images/findbyid.png rename to docs/Lectures/Backend/17/images/findbyid.png diff --git a/class-overview/Lecture-17/images/health.png b/docs/Lectures/Backend/17/images/health.png similarity index 100% rename from class-overview/Lecture-17/images/health.png rename to docs/Lectures/Backend/17/images/health.png diff --git a/class-overview/Lecture-17/images/sell.png b/docs/Lectures/Backend/17/images/sell.png similarity index 100% rename from class-overview/Lecture-17/images/sell.png rename to docs/Lectures/Backend/17/images/sell.png diff --git a/class-overview/Lecture-17/images/sell2.png b/docs/Lectures/Backend/17/images/sell2.png similarity index 100% rename from class-overview/Lecture-17/images/sell2.png rename to docs/Lectures/Backend/17/images/sell2.png diff --git a/class-overview/Lecture-17/images/tickets1.png b/docs/Lectures/Backend/17/images/tickets1.png similarity index 100% rename from class-overview/Lecture-17/images/tickets1.png rename to docs/Lectures/Backend/17/images/tickets1.png diff --git a/class-overview/Lecture-17/images/ticketsAll-1.png b/docs/Lectures/Backend/17/images/ticketsAll-1.png similarity index 100% rename from class-overview/Lecture-17/images/ticketsAll-1.png rename to docs/Lectures/Backend/17/images/ticketsAll-1.png diff --git a/class-overview/Lecture-17/images/ticketsAll-2.png b/docs/Lectures/Backend/17/images/ticketsAll-2.png similarity index 100% rename from class-overview/Lecture-17/images/ticketsAll-2.png rename to docs/Lectures/Backend/17/images/ticketsAll-2.png diff --git a/class-overview/Lecture-17/images/ticketsAll-3.png b/docs/Lectures/Backend/17/images/ticketsAll-3.png similarity index 100% rename from class-overview/Lecture-17/images/ticketsAll-3.png rename to docs/Lectures/Backend/17/images/ticketsAll-3.png diff --git a/class-overview/Lecture-17/images/ticketsAll-4.png b/docs/Lectures/Backend/17/images/ticketsAll-4.png similarity index 100% rename from class-overview/Lecture-17/images/ticketsAll-4.png rename to docs/Lectures/Backend/17/images/ticketsAll-4.png diff --git a/class-overview/Lecture-17/images/update.png b/docs/Lectures/Backend/17/images/update.png similarity index 100% rename from class-overview/Lecture-17/images/update.png rename to docs/Lectures/Backend/17/images/update.png diff --git a/class-overview/Lecture-17/images/username.png b/docs/Lectures/Backend/17/images/username.png similarity index 100% rename from class-overview/Lecture-17/images/username.png rename to docs/Lectures/Backend/17/images/username.png diff --git a/docs/Lectures/Backend/17/resource.md b/docs/Lectures/Backend/17/resource.md new file mode 100644 index 0000000..89d760e --- /dev/null +++ b/docs/Lectures/Backend/17/resource.md @@ -0,0 +1,43 @@ +# Lecture 17 - [Backend 4] Raffle Draw Project + +We will make a lottery app in this class. The details are given below: + +## Lottery API + +- sell lottery ticket +- update lottery ticket +- delete lottery ticket +- get all tickets +- get ticket by id +- bulk buy (user can buy multiple tickets at a time) +- raffle draw + +### Ticket + +- number (unique) +- username +- price +- timestamp + +#### Routes + +- /tickets/t/:ticketId GET - find single ticket +- /tickets/t/:ticketId PATCH - update ticket by id +- /tickets/t/:ticketId DELETE - delete ticket by id +- /tickets/u/:username GET - find tickets for a given user +- /tickets/u/:username PATCH - update tickets for a given user +- /tickets/u/:username DELETE - delete all tickets for a given user +- /tickets/sell - create tickets +- /tickets/bulk - bulk sell ticket +- /tickets/draw +- /tickets/ - find all tickets + +#### References + +- [Source Code](../../projects/raffle-draw/) +- [Class Overview](../../Class%20Overview/Lecture-17/README.md) + +#### Tasks + +- Make this app on your own way +- Go to this [link](https://www.mongodb.com/docs/manual/crud/) and study Insert documents, Query documents, Update documents and Delete documents diff --git a/class-overview/Lecture-18/README.md b/docs/Lectures/Backend/18/Overview.md similarity index 100% rename from class-overview/Lecture-18/README.md rename to docs/Lectures/Backend/18/Overview.md diff --git a/class-overview/Lecture-18/images/Screenshot_1.png b/docs/Lectures/Backend/18/images/Screenshot_1.png similarity index 100% rename from class-overview/Lecture-18/images/Screenshot_1.png rename to docs/Lectures/Backend/18/images/Screenshot_1.png diff --git a/class-overview/Lecture-18/images/Screenshot_2.png b/docs/Lectures/Backend/18/images/Screenshot_2.png similarity index 100% rename from class-overview/Lecture-18/images/Screenshot_2.png rename to docs/Lectures/Backend/18/images/Screenshot_2.png diff --git a/class-overview/Lecture-18/images/data-duplication.png b/docs/Lectures/Backend/18/images/data-duplication.png similarity index 100% rename from class-overview/Lecture-18/images/data-duplication.png rename to docs/Lectures/Backend/18/images/data-duplication.png diff --git a/class-overview/Lecture-18/images/document-oriented.png b/docs/Lectures/Backend/18/images/document-oriented.png similarity index 100% rename from class-overview/Lecture-18/images/document-oriented.png rename to docs/Lectures/Backend/18/images/document-oriented.png diff --git a/class-overview/Lecture-18/images/key_value.png b/docs/Lectures/Backend/18/images/key_value.png similarity index 100% rename from class-overview/Lecture-18/images/key_value.png rename to docs/Lectures/Backend/18/images/key_value.png diff --git a/class-overview/Lecture-18/images/wide_column.png b/docs/Lectures/Backend/18/images/wide_column.png similarity index 100% rename from class-overview/Lecture-18/images/wide_column.png rename to docs/Lectures/Backend/18/images/wide_column.png diff --git a/docs/Lectures/Backend/18/resource.md b/docs/Lectures/Backend/18/resource.md new file mode 100644 index 0000000..425f38a --- /dev/null +++ b/docs/Lectures/Backend/18/resource.md @@ -0,0 +1,30 @@ +# Lecture 18 - [Backend 5] Understand The Concepts of Database + +## Database Paradigms + +1. Key Value Database + - [Redis](https://redis.io/) + - [MEMcached](https://www.memcached.org/) + - [Amazon DynamoDB](https://aws.amazon.com/dynamodb/) +2. Wide-Column Database + - [Cassandra](https://cassandra.apache.org/_/index.html) + - [Apache Hbase](https://hbase.apache.org/) +3. Document Oriented Database + - [MongoDB](https://mongodb.com/) + - [Firebase](https://firebase.google.com/docs/firestore/) +4. The Relational Database + - [MySQL](https://www.mysql.com/) + - [PostgreSQL](https://www.postgresql.org/) + - [Microsoft SQL](https://docs.microsoft.com/en-us/sql/) +5. Graph Database + - [Neo4J](https://neo4j.com/) +6. A Full Text Search Engine + - [Algolia](https://www.algolia.com/) + - [Elastic Search](https://www.elastic.co/elasticsearch/) +7. Multi Model Database + - [Fauna DB](https://fauna.com/) + +### References + +- [7 Database Paradigms](https://tudip.com/blog-post/7-database-paradigms/) +- [Class Overview](../../Class%20Overview/Lecture-18/README.md) diff --git a/class-overview/Lecture-19/README.md b/docs/Lectures/Backend/19/Overview.md similarity index 100% rename from class-overview/Lecture-19/README.md rename to docs/Lectures/Backend/19/Overview.md diff --git a/resources/lecture-19/README.md b/docs/Lectures/Backend/19/resource.md similarity index 100% rename from resources/lecture-19/README.md rename to docs/Lectures/Backend/19/resource.md diff --git a/class-overview/Lecture-20/README.md b/docs/Lectures/Backend/20/Overview.md similarity index 100% rename from class-overview/Lecture-20/README.md rename to docs/Lectures/Backend/20/Overview.md diff --git a/class-overview/Lecture-20/images/database-1.png b/docs/Lectures/Backend/20/images/database-1.png similarity index 100% rename from class-overview/Lecture-20/images/database-1.png rename to docs/Lectures/Backend/20/images/database-1.png diff --git a/class-overview/Lecture-20/images/db-2.png b/docs/Lectures/Backend/20/images/db-2.png similarity index 100% rename from class-overview/Lecture-20/images/db-2.png rename to docs/Lectures/Backend/20/images/db-2.png diff --git a/class-overview/Lecture-20/images/db-3.png b/docs/Lectures/Backend/20/images/db-3.png similarity index 100% rename from class-overview/Lecture-20/images/db-3.png rename to docs/Lectures/Backend/20/images/db-3.png diff --git a/class-overview/Lecture-20/images/entity-diagram.png b/docs/Lectures/Backend/20/images/entity-diagram.png similarity index 100% rename from class-overview/Lecture-20/images/entity-diagram.png rename to docs/Lectures/Backend/20/images/entity-diagram.png diff --git a/class-overview/Lecture-20/images/error.png b/docs/Lectures/Backend/20/images/error.png similarity index 100% rename from class-overview/Lecture-20/images/error.png rename to docs/Lectures/Backend/20/images/error.png diff --git a/class-overview/Lecture-20/images/example.png b/docs/Lectures/Backend/20/images/example.png similarity index 100% rename from class-overview/Lecture-20/images/example.png rename to docs/Lectures/Backend/20/images/example.png diff --git a/class-overview/Lecture-20/images/mongodb-1.png b/docs/Lectures/Backend/20/images/mongodb-1.png similarity index 100% rename from class-overview/Lecture-20/images/mongodb-1.png rename to docs/Lectures/Backend/20/images/mongodb-1.png diff --git a/class-overview/Lecture-20/images/schematypes.png b/docs/Lectures/Backend/20/images/schematypes.png similarity index 100% rename from class-overview/Lecture-20/images/schematypes.png rename to docs/Lectures/Backend/20/images/schematypes.png diff --git a/class-overview/Lecture-20/images/success.png b/docs/Lectures/Backend/20/images/success.png similarity index 100% rename from class-overview/Lecture-20/images/success.png rename to docs/Lectures/Backend/20/images/success.png diff --git a/docs/Lectures/Backend/20/resource.md b/docs/Lectures/Backend/20/resource.md new file mode 100644 index 0000000..78c3e7a --- /dev/null +++ b/docs/Lectures/Backend/20/resource.md @@ -0,0 +1,13 @@ +# Lecture 20 - [Backend 7] Start Working with Mongoose + +This video was part of our backend BootCamp. In this video, we discussed Mongoose the best ORM for MongoDB. + +## References + +- [MongooseJs](https://mongoosejs.com/) +- [Source Code](../../src/mongo-demo/) +- [Class Overview](../../Class%20Overview/Lecture-20/README.md) + +## Tasks + +- Study and practice Schemas, SchemaTypes, Connections, Models, Documents, Subdocuments, Queries and Validation from [MongooseJs Docs](https://mongoosejs.com/docs/) diff --git a/class-overview/Lecture-21/README.md b/docs/Lectures/Backend/21/Overview.md similarity index 100% rename from class-overview/Lecture-21/README.md rename to docs/Lectures/Backend/21/Overview.md diff --git a/class-overview/Lecture-27/Client Server.drawio b/docs/Lectures/Frontend/27/Client Server.drawio similarity index 100% rename from class-overview/Lecture-27/Client Server.drawio rename to docs/Lectures/Frontend/27/Client Server.drawio diff --git a/class-overview/Lecture-27/README.md b/docs/Lectures/Frontend/27/Overview.md similarity index 100% rename from class-overview/Lecture-27/README.md rename to docs/Lectures/Frontend/27/Overview.md diff --git a/class-overview/Lecture-27/images/2021stateofjs.png b/docs/Lectures/Frontend/27/images/2021stateofjs.png similarity index 100% rename from class-overview/Lecture-27/images/2021stateofjs.png rename to docs/Lectures/Frontend/27/images/2021stateofjs.png diff --git a/class-overview/Lecture-27/images/Frontend-Layer.jpg b/docs/Lectures/Frontend/27/images/Frontend-Layer.jpg similarity index 100% rename from class-overview/Lecture-27/images/Frontend-Layer.jpg rename to docs/Lectures/Frontend/27/images/Frontend-Layer.jpg diff --git a/class-overview/Lecture-27/images/backend-client.jpg b/docs/Lectures/Frontend/27/images/backend-client.jpg similarity index 100% rename from class-overview/Lecture-27/images/backend-client.jpg rename to docs/Lectures/Frontend/27/images/backend-client.jpg diff --git a/class-overview/Lecture-27/images/drawio-ext.png b/docs/Lectures/Frontend/27/images/drawio-ext.png similarity index 100% rename from class-overview/Lecture-27/images/drawio-ext.png rename to docs/Lectures/Frontend/27/images/drawio-ext.png diff --git a/class-overview/Lecture-27/images/react-overview.jpg b/docs/Lectures/Frontend/27/images/react-overview.jpg similarity index 100% rename from class-overview/Lecture-27/images/react-overview.jpg rename to docs/Lectures/Frontend/27/images/react-overview.jpg diff --git a/class-overview/Lecture-28/README.md b/docs/Lectures/Frontend/28/Overview.md similarity index 100% rename from class-overview/Lecture-28/README.md rename to docs/Lectures/Frontend/28/Overview.md diff --git a/class-overview/Lecture-29/README.md b/docs/Lectures/Frontend/29/Overview.md similarity index 100% rename from class-overview/Lecture-29/README.md rename to docs/Lectures/Frontend/29/Overview.md diff --git a/class-overview/Lecture-29/images/atoms.jpg b/docs/Lectures/Frontend/29/images/atoms.jpg similarity index 100% rename from class-overview/Lecture-29/images/atoms.jpg rename to docs/Lectures/Frontend/29/images/atoms.jpg diff --git a/class-overview/Lecture-29/images/blank.png b/docs/Lectures/Frontend/29/images/blank.png similarity index 100% rename from class-overview/Lecture-29/images/blank.png rename to docs/Lectures/Frontend/29/images/blank.png diff --git a/class-overview/Lecture-29/images/div.png b/docs/Lectures/Frontend/29/images/div.png similarity index 100% rename from class-overview/Lecture-29/images/div.png rename to docs/Lectures/Frontend/29/images/div.png diff --git a/class-overview/Lecture-29/images/dynamic.png b/docs/Lectures/Frontend/29/images/dynamic.png similarity index 100% rename from class-overview/Lecture-29/images/dynamic.png rename to docs/Lectures/Frontend/29/images/dynamic.png diff --git a/class-overview/Lecture-29/images/h1.png b/docs/Lectures/Frontend/29/images/h1.png similarity index 100% rename from class-overview/Lecture-29/images/h1.png rename to docs/Lectures/Frontend/29/images/h1.png diff --git a/class-overview/Lecture-29/images/molecule.jpg b/docs/Lectures/Frontend/29/images/molecule.jpg similarity index 100% rename from class-overview/Lecture-29/images/molecule.jpg rename to docs/Lectures/Frontend/29/images/molecule.jpg diff --git a/class-overview/Lecture-29/images/organism-examples.jpg b/docs/Lectures/Frontend/29/images/organism-examples.jpg similarity index 100% rename from class-overview/Lecture-29/images/organism-examples.jpg rename to docs/Lectures/Frontend/29/images/organism-examples.jpg diff --git a/class-overview/Lecture-29/images/p.png b/docs/Lectures/Frontend/29/images/p.png similarity index 100% rename from class-overview/Lecture-29/images/p.png rename to docs/Lectures/Frontend/29/images/p.png diff --git a/class-overview/Lecture-29/images/page1.jpg b/docs/Lectures/Frontend/29/images/page1.jpg similarity index 100% rename from class-overview/Lecture-29/images/page1.jpg rename to docs/Lectures/Frontend/29/images/page1.jpg diff --git a/class-overview/Lecture-29/images/product_hunt.png b/docs/Lectures/Frontend/29/images/product_hunt.png similarity index 100% rename from class-overview/Lecture-29/images/product_hunt.png rename to docs/Lectures/Frontend/29/images/product_hunt.png diff --git a/class-overview/Lecture-29/images/react-1.png b/docs/Lectures/Frontend/29/images/react-1.png similarity index 100% rename from class-overview/Lecture-29/images/react-1.png rename to docs/Lectures/Frontend/29/images/react-1.png diff --git a/class-overview/Lecture-29/images/react-2.png b/docs/Lectures/Frontend/29/images/react-2.png similarity index 100% rename from class-overview/Lecture-29/images/react-2.png rename to docs/Lectures/Frontend/29/images/react-2.png diff --git a/class-overview/Lecture-29/images/react-3.png b/docs/Lectures/Frontend/29/images/react-3.png similarity index 100% rename from class-overview/Lecture-29/images/react-3.png rename to docs/Lectures/Frontend/29/images/react-3.png diff --git a/class-overview/Lecture-29/images/react-4.png b/docs/Lectures/Frontend/29/images/react-4.png similarity index 100% rename from class-overview/Lecture-29/images/react-4.png rename to docs/Lectures/Frontend/29/images/react-4.png diff --git a/class-overview/Lecture-29/images/react-5.png b/docs/Lectures/Frontend/29/images/react-5.png similarity index 100% rename from class-overview/Lecture-29/images/react-5.png rename to docs/Lectures/Frontend/29/images/react-5.png diff --git a/class-overview/Lecture-29/images/style.png b/docs/Lectures/Frontend/29/images/style.png similarity index 100% rename from class-overview/Lecture-29/images/style.png rename to docs/Lectures/Frontend/29/images/style.png diff --git a/class-overview/Lecture-29/images/template1.jpg b/docs/Lectures/Frontend/29/images/template1.jpg similarity index 100% rename from class-overview/Lecture-29/images/template1.jpg rename to docs/Lectures/Frontend/29/images/template1.jpg diff --git a/docs/Lectures/Fundamentals/01/Overview.md b/docs/Lectures/Fundamentals/01/Overview.md new file mode 100644 index 0000000..333e9ec --- /dev/null +++ b/docs/Lectures/Fundamentals/01/Overview.md @@ -0,0 +1,212 @@ +## SRS (Software Requirements Specifications) + +ধরে নিলাম আমরা প্রত্যেকেই একেকজন উদ্যোক্তা। আমরা একটা ব্যবসা শুরু করতে যাচ্ছি। সে ব্যবসার নাম হলো Problem Solvers Caffe (PS Caffe)। এটা মূলত যারা প্রোগ্রামার বা problem solvers তাদের জন্য একটা বিশেষ ক্যাফে আমরা শুরু করতে যাচ্ছি। + +এর requirements আছে কিছু। এই ধরণের রিকোয়ারমেন্টসকে বলা হয় SRS (Software Requirements Specifications)। এই অ্যাপের জন্য SRS হলোঃ + +> PS Caffe is an imaginary online coffee delivery service. This business is designed specially for programmers by keeping their life style in mind. We need an application to accept orders from online. The application will have the following functional and non functional requirements. +> **Special Notes:** We are not planning to grow fast. We need an [MVP (Minimal Viable Product)](https://en.wikipedia.org/wiki/Minimum_viable_product) or Workable Prototype to research our targeted market. + +খুবই সিম্পল একটা অ্যাপ্লিকেশন। আমরা প্রোগ্রামারদের লাইফস্টাইল মাথায় রেখে আমরা যেখানে আছি সেখানে একটি কফি ডেলিভারি সার্ভিস তৈরি করতে চাইছি। যেহেতু আমরা প্রোগ্রামারদের লাইফস্টাইল চিন্তায় রেখে তা করতে চাইছি তার মানে আমাদের ২৪/৭ সার্ভিস দিতে হবে। এটি একটি অনলাইনভিত্তিক সার্ভিস। একটি স্পেশাল নোট আছে যাতে লেখা আছে আমরা খুব দ্রুত ব্যবসাটাকে বাড়াতে চাইছি না। আমরা শুধু মার্কেট রিসার্চের জন্য একটি MVP (Minimal Viable Product) or Workable prototype তৈরি করতে চাইছি। এই MVP এবং রিয়েল লাইফ অ্যাপ্লিকেশনের মধ্যে অনেক বিশাল একটা পার্থক্য রয়েছে। এটা বলে নেয়া জরুরী MVP টার্মটি শুধু যে সফটওয়্যার ডেভেলপমেন্টের সাথে জড়িত এমন নয়। এর বাইরে যেকোনো ফিল্ডেই এটা ব্যবহার করা যেতে পারে এবং ব্যবহার হচ্ছে। + +MVP কে আমরা অন্য ভাষায় Workable Prototype ও বলে থাকি। প্রোটোটাইপ দুই ধরণের হয়। এক ধরণের প্রোটোটাইপ যার শুধুমাত্র UI দেখা যাবে কিন্তু কোনো কাজ করা যাবে না। আর আমাদেরটা হলো ওয়ার্কেবল প্রোটোটাইপ। এর মানে আমরা এমন একটা অ্যাপ্লিকেশন বানাবো যার মাধ্যমে আমরা ২০০/৩০০ মানুষকে সার্ভিস দিতে পারবো। তাহলে প্রথম ভার্সনের জন্য অনেক ফাংশনালিটিজেরই প্রয়োজন হবে না। পরে ব্যবসা বৃদ্ধির সাথে সাথে আমরা এই জিনিসগুলোকে আপডেট করতে থাকবো। + +যেহেতু আমরা MVP বানাচ্ছি এবং ভবিষ্যতে আমাদের ব্যবসা বৃদ্ধি করার কথাও মাথায় আছে, সুতরাং আমাদের খেয়াল রাখতে হবে যেন আমাদের অ্যাপ্লিকেশনটা পরবর্তীতে যেকোনো আর্কিটেকচারে সহজেই ট্রান্সফার করতে পারি সে ব্যবস্থা রাখা। + +এই অ্যাপ্লিকেশনের কিছু funtional and non-functional requirements আছে। ইন্টারভিউতে গুরুত্বপূর্ণ একটি প্রশ্ন থাকে What is the difference between functional and non-functional requirements. + +### ফাংশনাল রিকোয়ারমেন্টস + +ফাংশনাল রিকোয়ারমেন্টস হলো এমন কিছু রিকোয়ারমেন্টস যা আমার অ্যাপ্লিকেশনের ফিচারে উপর ডিপেন্ড করে। এগুলো সাধারণত ক্লায়েন্ট বলে দেয়। আবার নাও বলে দিতে পারে। যেমন ক্লায়েন্ট হয়তো বললো অ্যাপ্লিকেশন লগিনের কথা। লগ আউটের কথা সে বললোই না। কিন্তু লগিন থাকলে অবশ্যই সেখানে লগ আউট থাকবেই। সুতরাং ক্লায়েন্ট বলে না দিলেও অ্যাপ্লিকেশনের খাতিরে আমাদের কিছু কিছু ফাংশনাল রিকোয়ারমেন্ট নিজেদের যুক্ত করতে হবে। এই অ্যাপ্লিকেশনের ফাংশনাল রিকোয়ারমেন্টস আছে কিছু। সেগুলো হলোঃ + +> **Functional Requirements (Mostly client requirements):** +> +> - Local Authentication: +> At the beginning we don't want to spend more on authentication services. Just keep it simple by implementing a local authentication using email & password. But make sure, we can extend local AUTH to OAuth2 anytime in future. We need the following features - +> - Hashed password +> - Email verification +> - Forget password +> - Block users if necessary + +প্রথমে আমাদের কোনো ধরণের পেইড অথেনটিকেশন সার্ভিসের প্রয়োজন নেই। আমরা শুধু ইমেইল আর পাসওয়ার্ড দিয়ে লোকাল অথেনটিকেশন করবো। তবে আমাদের খেয়াল রাখতে হবে যেন ভবিষ্যতে আমরা যেন local AUTH to OAuth2 তে এক্সটেন্ড করতে পারি এই সিস্টেম রাখা। এটা আমরা যখন মডেল তৈরি করবো বা কন্ট্রোলার তৈরি করবো তখন এরকম একটা ব্যবস্থা আমাদের রাখতে হবে। এছাড়া আমাদের কিছু ফিচার রাখতে হবে যেমন - পাসওয়ার্ড হ্যাশ করা, ইমেইল ভেরিফিকেশন, ফরগেট পাসওয়ার্ড এবং যখন চাইবো তখন যেন আমরা যেকোনো ইউজারকে ব্লক করে দিতে পারি। + +> - Multiple Roles (Role Based Access Control): +> There will be mainly five roles: +> - Admin: Admin can create and manage everything including sales data +> - Manager: Manager can't be able to create anything but can see sales data, inventory and products +> - Chef: They can only see queue orders +> - Delivery Man: They can manage the queue orders and change status +> - User: won't able to see any admin information but able to check products, reviews and place orders. + +এই অ্যাপ্লিকেশনে ৫টা মেইন রোল থাকবে। অ্যাডমিন সবকিছুতে এক্সেস নিতে পারবে। ম্যানেজার শুধু সেলস ডাটা, ইনভেনটরি আর প্রোডাক্ট দেখতে পারবে। শেফ শুধু যেসব অর্ডার কিউতে আছে তা দেখতে পারবে। ডেলিভারি ম্যান অর্ডার ম্যানেজ করতে পারবে আর স্ট্যাটাস চেইঞ্জ করতে পারবে। যেমন ডেলিভারির জন্য বের হলে সে স্ট্যাটাস চেইঞ্জ করে out for delivery, ডেলিভারি হয়ে গেলে Delivered এরকম স্ট্যাটাস চেইঞ্জ করতে পারবে। ইউজার কখনও অ্যাডমিন ইনফরমেশন দেখতে পারবে না, শুধু প্রোডাক্ট চেক করতে পারবে, অর্ডার প্লেইস করতে পারবে এবং রিভিউ দিতে পারবে। + +> - User will be able to place orders + +ইউজার অর্ডার প্লেইস করতে পারবে নিজের পছন্দমতো। + +> - User will be able see existing reviews and only place review after a successful order + +ইউজার এক্সিস্টিং রিভিউগুলো দেখতে পারবে। কিন্তু নিজে ততক্ষণ পর্যন্ত রিভিউ দিতে পারবে না যতক্ষণ পর্যন্ত একটা অর্ডার দিবে না এবং সেই অর্ডারের ডেলিভারি কমপ্লিট হবে না। প্রতিটা অর্ডারের বিপরীতে শুধু সেই অর্ডারের রিভিউ দিতে পারবে। + +> - Sales dashboard + +এখান থেকে সেলস স্ট্যাটাস জানা যাবে। যে কত বিক্রি হলো, এই সপ্তাহে বেশি হলো না কম এসব ডাটা অ্যানালাইসিস করা যাবে এই ড্যাশবোর্ড থেকে। + +> - Manage inventory + +এখানে থেকে সমস্ত ইনভেনটরির ট্র্যাকিং রাখা যাবে। যেমন চিনি, কফি, দুধ ইত্যাদির মজুত কি পরিমাণ আছে তা এখানে রেকর্ড থাকবে। এছাড়াও এমন একটি ফিচার অ্যাড করা যায়, যেমন একটা ক্যাপাচিনো বানাতে কি পরিমাণ কফি, চিনি বা দুধ লাগে তা একটা অর্ডারের বিপরীতে বাদ দিয়ে বাকিটা শো করানো যায়। + +> - Live tracking of the order + +ইউজার তার অর্ডারটা লাইভ ট্র্যাকিং করতে পারবে। যেমন অর্ডার প্লেইস হলে অর্ডার প্লেইসড এরকম একটা স্ট্যাটাস দেখাবে। অনুরূপভাবে যখন কফি বানানো হচ্ছে তখন প্রসেসিং, ডেলিভারির জন্য বের হলে আউট ফর ডেলিভারি এরকম কিছু স্ট্যাটাস সে দেখতে পারবে। পরবর্তীতে অ্যাপ্লিকেশনের গ্রোথের উপর নির্ভর করে গুগল ম্যাপের API কানেক্ট করে লাইভ লোকেশন ট্র্যাকিং এর ব্যবস্থাও করা যেতে পারে। + +এই গেলো আমাদের ফাংশনাল রিকোয়ারমেন্টস। + +### নন ফাংশনাল রিকোয়ারমেন্টস + +নন ফাংশনাল রিকোয়ারমেন্টস হলো এমন কিছু রিকোয়ারমেন্টস যা অ্যাপ্লিকেশনের ফিচার কেমন হবে না হবে তার উপর ডিপেন্ড করে না। আর এই নন ফাংশনাল রিকোয়ারমেন্টসগুলো প্রায় সব অ্যাপ্লিকেশনের জন্য একই হয়। এগুলো মূলত আমাদের অ্যাপ্লিকেশন কতো সুন্দরভাবে, সেইফলি পারফর্ম করবে তা নিশ্চিত করে। যেমন এই অ্যাপ্লিকেশনের কিছু নন ফাংশনাল রিকোয়ারমেন্টস আছে। সেগুলো হলোঃ + +> **Non Functional Requirements:** + +> - Secure + +একটা অ্যাপ্লিকেশন বানাতে গেলে অবশ্যই তা সিকিউর হতে হবে। আমরা কেউই চাই না আমার অ্যাপ্লিকেশনে যে কেউ ঢুকে যা খুশি তা করে ফেলবে। তাই প্রয়োজন অনুসারে অ্যাপ্লিকেশন সিকিউর করতে হবে। + +> - Reliable + +অবশ্যই আমাদের অ্যাপ্লিকেশন এমন হতে হবে যেন সবাই rely করতে পারে। ধরুন কেউ একটা কফি অর্ডার করলো। ৩০ মিনিট পর ঢুকে দেখলো তার ড্যাশবোর্ড থেকে অর্ডার উধাও। কিন্তু তার টাকা কেটে ফেলেছে। এরকম হলে কেউই এই অ্যাপ্লিকেশনের উপর রিলাই করতে পারবেনা। সুতরাং আমাদের Data secure হতে হবে, reliable হতে হবে এবং Data consistency বজায় রাখতে হবে। + +> - Easy Maintainability + +MVP এমন একটি প্রোডাক্ট, যে প্রোডাক্ট আমরা পাবলিক রিসার্চের জন্য বানিয়েছি। সুতরাং এতে অনেক বাগ থাকবে এটা নিশ্চিত। এছাড়াও দিনে দিনে কাস্টমারের অনেক রিকোয়ারমেন্ট বাড়তে পারে। তারা ব্যবহার করে আমাদের রিকোয়ারমেন্ট দিলো যে এই ফিচারটা হলে ভাল হয়। আমরাও ব্যবসার ভালর জন্য দেখলাম কিছু ফিচার যোগ করলে ব্যবসা আরো ভাল হবে। এসব দিক মাথায় রেখে অবশ্যই আমাদের অ্যাপ্লিকেশনটি বানাতে হবে যেন প্রয়োজনে যেকোনো ফিচার সহজে অ্যাড করা যায়, সহজে যেকোনো বাগ ফিক্স করা যায়। + +> - Awesome Usability + +এটি পুরোপুরি একটি UX টার্ম। আমার অ্যাপ্লিকেশন যদি ইউজার সহজে ইউজ করতে না পারে তাহলে তা যত ভাল ফিচার্সই দিকনা কেন তা কেউই ইউজ করবে না। সুতরাং Usability অবশ্যই ভাল হতে হবে। + +> - High availability (not main concern for MVP) + +এর মানে হলো দুনিয়াতে যাই ঘটে যাক আমাদের অ্যাপ্লিকেশন ২৪/৭ অ্যাভাইলেবল থাকতে হবে। এখানে একটা কনসেপ্ট আছে - ইলেভেন নাইন (৯৯.৯৯৯৯৯৯৯৯৯%)। এর মানে হচ্ছে সবথেকে বেশি অ্যাভেইলেবল। এর চেয়ে বেশি অ্যাভেইলেবল হওয়া সম্ভব না। এগুলো মূলত ক্লাউড নিয়ে যারা কাজ করেন তারা এর সাথে পরিচিত। আমাদের খুঁজে বের করতে হবে অ্যামাজনের কোনো সার্ভিসগুলো কতটা অ্যাভাইলেবিলিটি দিচ্ছে তার জন্য এই কনসেপ্ট আমাদের লাগবে। যদিও এই ফাংশনালিটি MVP এর জন্য মেইন কনসার্ন নয়। আমরা জাস্ট কিছু ইউজারদের জন্য এই প্রোডাক্ট বানাবো। এরপর অ্যাপ্লিকেশনের ইউজের উপর ডিপেন্ড করে আমরা আমাদের অ্যাপ্লিকেশনের availablity বাড়াবো। + +> - Scalability (not main concern for MVP) + +এটাও MVP এর জন্য মেইন কনসার্ন না। ধরুন আমাদের অ্যাপ্লিকেশনের ইউজার দিন দিন বাড়তে থাকলো। শুরুতে ১০ জন। তারপরের মাসে ১০০ জন, এরপর ১০০০, ১০০০০ ইত্যাদি। এর সাথে সাথে আমাদের অ্যাপ্লিকেশনকেও scalable করতে হবে এত বিপুল পরিমাণ ইউজারদের জন্য। সার্ভার একাধিক ইউজ করতে হতে পারে। এটা যদিও MVP এর জন্য লাগে না, কিন্তু অ্যাপ্লিকেশন বানাতে গেলে এই জিনিসটা মাথায় রাখা প্রয়োজন। + +মূলত এগুলোই আমাদের নন ফাংশনালিটিজ রিকোয়ারমেন্টস। + +## SDLC Models + +প্রজেক্ট ম্যানেজমেন্টের জন্য আমরা সাধারণত দুইটা মডেল নিয়ে কাজ করি। Water Fall Model এবং Agile Model. এই দুইটা ছাড়াও আরো অসংখ্য মডেল আছে। আপনি SDLC (Software Development Life Cycle) model লিখে সার্চ দিলে আরো অসংখ্য মডেল পাবেন। কিন্তু এই দুইটি মডেল বছরের পর বছর ধরে সফলতার সাথে ব্যবহার হয়ে আসছে। +এখন প্রশ্ন হলো কোন মডেল আমরা ব্যবহার করবো। তার জন্য আমাদের এই দুইটি মডেল সম্পর্কে একটু ধারণা থাকা দরকার। + +### Water Fall Model + +ধরুন আমাদের রিকোয়ামেন্টস ফিক্সড। আগামী ৪/৫ বছরেও রিকোয়ারমেন্ট চেইঞ্জ হবে না। আমরা এই রিকোয়ারমেন্ট অনুসারে প্রোডাক্ট বানাবো। এরপর ৪/৫ বছর পর যদি প্রয়োজন হয় আপডেট করবো। এই ধরণের প্রজেক্টের ক্ষেত্রে ওয়াটার ফল মডেল খুবই কার্যকরী। +Water Fall Model + +উপরের ছবি থেকে আমরা বিষয়টা বুঝতে পারবো। আমরা এতক্ষণ পর্যন্ত যা করলাম তা সব Requirements এর অন্তর্গত। এখন এই রিকোয়ারমেন্টস অ্যানালাইসিস করে কোন আর্কিটেকচার ইউজ করতে হবে, কিভাবে আর্কিটেক্ট করতে হবে, কোন সিস্টেম ডিজাইন ইউজ করতে হবে, কি কি চ্যালেঞ্জ আসতে পারে, কি কি constrains আসতে পারে, কিভাবে আমরা এই প্রব্লেমগুলোকে সলভ করতে পারি, কিভাবে আমরা টাইম কমিয়ে আনতে পারি এই সবগুলো Design এর আলোচ্য বিষয়। এরপর আমরা UI/UX করি, ফ্রন্টএন্ড ডেভেলপমেন্ট করি, ব্যাকএন্ড ডেভেলপমেন্ট করি, এপিআই বানাই সবকিছু Implementation এর অন্তর্গত। এরপর আমাদের বিভিন্ন টেস্টিং এর মধ্য দিয়ে যেতে হবে। লোড টেস্ট, একসেপ্টেন্স টেস্ট, সিকিউরিটি টেস্ট ইত্যাদি টেস্ট পাশ করলে এরপর আমাদের অ্যাপ্লিকেশন আমরা ডেপ্লয় করতে পারি। এসমস্ত টপিক Verification এর অন্তর্গত। অ্যাপ্লিকেশন বানালে অবশ্যই বাগ থাকবেই। এই বাগ ডিবাগ করা Maintanence এর অন্তর্ভুক্ত। + +এই মডেলের একটা বিরাট প্রব্লেম আছে। প্রব্লেমটা হলো ধরেন রিকোয়ারমেন্টস অ্যানালাইসিস শেষ, সেই অনুযায়ী ডিজাইনও শেষ। ইমপ্লিমেন্টেশনের ৫০% শেষ। এখন ক্লায়েন্ট এসে এমন একটা চেইঞ্জের কথা বললো যে পুরো সিস্টেমই ব্রেক হয়ে গেলো। এমন হলে আবার প্রথম থেকে সবকিছু শুরু করতে হবে। আপনার এতদিনের কষ্ট সব মাঠে মারা গেলো। + +### Agile Model + +উপরের মডেলে যে সমস্যার কথা আমরা বলেছিলাম সে সমস্যা থেকে পরিত্রাণের উপায় হলো Agile Model। এখন প্রশ্ন হলো What is Agile Model. Agile model হলো এক ধরণের Water Fall Model. ওয়াটার ফল মডেলে আমরা যে কাজটা করি তা একটা লং টার্মের জন্য। ২/৩ বছরের জন্য। অপরদিকে Agile model হলো ১৫ দিন বা ৭ দিনের জন্য একটা ওয়াটার ফল মডেল। ৭ দিনের জন্য একটা রিকোয়ারমেন্ট থাকবে। সেই অনুযায়ী ডিজাইন, ইমপ্লিমেন্টেশন, ভেরিফিকেশন হবে। পরবর্তী ৭ দিন অন্য রিকোয়ারমেন্ট নিয়ে কাজ হবে। এভাবে চক্রটা চলতে থাকবে। যেমন আমাদের অ্যাপ্লিকেশনে ধরেন আমরা প্রথম ১৫ দিন লোকাল অথেনটিকেশন নিয়ে কাজ করবো। পরবর্তী ১৫ দিন রোল বেইজড এক্সেস কন্ট্রোল নিয়ে কাজ করবো। এভাবে চলতে থাকবে। এখন দুই মাস পর যদি ক্লায়েন্ট এসে বলে অথেনটিকেশনে ইমেইল দিয়ে করা হইসে। এখন সে ফোন নাম্বারও অ্যাড করতে চাইছে। যেহেতু আমরা আলাদাভাবেই অথেনটিকেশন নিয়ে কাজ করেছিলাম সুতরাং শুধু সেটাই চেইঞ্জ করলে হবে। বাকি কাজের উপর কোনো সমস্যা আসবে না। জাস্ট ওয়াটার ফল মডেলই একটা নির্দিষ্ট শর্ট টাইম পর পর। +Agile Model +যেহেতু আমাদের অ্যাপ্লিকেশনটি MVP সুতরাং চোখ বন্ধ করে আমরা Agile Model সিলেক্ট করবো। + +## Choose Necessary Technology + +আমাদের অ্যাপ্লিকেশন ডেভেলপমেন্টের জন্য আমাদের বুঝতে হবে কোন টেকনোলজি আমাদের এই অ্যাপ্লিকেশন ডেভেলপমেন্টের জন্য উপযুক্ত। তার জন্য আমাদের একটা ল্যান্ডস্কেপ দরকার। এখানে একটা Overview দেয়া হলো যাতে একটা আইডিয়া আপনারা পেতে পারেন। + +প্রথমেই আমাদের দরকার SRS তৈরি করা। তার জন্য আমাদের যে যে টুলস লাগতে পারে তা হলোঃ + +- Google Docs, Microsoft Office, Notion. +- (To draw EML Diagram, ER Diagram, Activity Diagram etc.) draw.io, Lucid chart + +এরপর আমরা জাম্প করবো ডিজাইন পার্টে। এটাকে বলা হয় যেকোনো অ্যাপ্লিকেশনের Blue Print. এই পার্টে যা থাকবে তা হলোঃ + +- System Design + + - Architectural Decision + + - Monolithic (Layered) + - Easy to develop, but very complex to maintain when application grows + - Microservice + - Very hard to develop, but easy to maintain. + - Serverless (FAAS - Function as a service) + + Monolithic Application হলো একটা অ্যাপ্লিকেশনের সব ফিচার্স এক জায়গাতেই থাকবে। অপরদিকে Microservice হলো ঐ অ্যাপ্লিকেশনের সব ফিচারকে আলাদা করে ফেলে আলাদা আলাদা সার্ভারে রাখা। যেহেতু আলাদা আলাদা ডাটাবেইজ থাকবে সেহেতু তাদের মধ্যে কমিউনিকেশন সিস্টেম তৈরি করা একটু কঠিন। তাই সিঙ্গেল পার্সন বা ছোট টীমের জন্য Microservice avoid করাই ভাল। + + যদি MVP হয় তাহলে চোখ বন্ধ করে Monolithic Architecture এ যেতে হবে। আর যেখানে বলা হবে আমার high availability and high scalability দরকার সেখানে আমরা মাইক্রোসার্ভিস ব্যবহার করবো। + + Serverless Architecture আমরা ইউজ করবো তখন, যখন ক্লায়েন্ট বলবে আমি MVP চাইছি, কিন্তু খুব সম্ভাবনা আছে এই অ্যাপ্লিকেশন খুব তাড়াতাড়ি গ্রো করার। যদি গ্রো করে ফেলে খুব তাড়াতাড়ি আমি চাই না কোনো ধরণের কোনো সমস্যা ইউজার ফেইস করুক বা ইউজার কোনো অভিযোগ আমাদের জানাক। কারণ এটা হাইলি স্কেলেবল। + + - Select Database + অনেক রকমের ডাটাবেইজ হয়েছে মার্কেটে। কোন কাজের জন্য কোন ধরণের ডাটাবেইজ লাগবে তা আমাদের খুঁজে বের করতে হবে। + - SQL (যখন আমি জানি যে ডাটাতে কি কি প্রোপার্টি থাকবে না থাকবে) + - PostgreSQL + - MySQL + - NoSQL (যখন রিকোয়ারমেন্ট চেইঞ্জ হবে র‍্যানডমলি) + - MongoDB + - Casandra + - Key value + - Redis (ক্যাশিং, জব শিডিউলিং, ম্যাসেজ ব্রোকার) + - DynamoDB + - Graph Database (গ্রাফ স্ট্রাকচারের ডাটাগুলোর জন্য) + - Neo4j + - Search Database (সার্চ ইঞ্জিন রিলেটেড কাজ করে) + - Elastic Database (Open Source - Text Search) + - Algolia Search (Open AI 3 - Natural Language Processing) যেমন আমি সার্চ করলাম Men's favorite pet তখন সে আমার লেখা পড়ে মানুষের প্রিয় পোষা প্রাণী কি হতে পারে সেটা সার্চ করে দিবে। এটা অনেক পাওয়ারফুল কিন্তু ফ্রি নয়। + +- Software Architecture + Software Architecture ডিরেক্টলি রিলেটেড টু কোডিং। এটা পুরোপুরি ডিপেন্ড করে সিস্টেম ডিজাইনের উপর। সফটওয়্যার আর্কিটেকচারের জন্য আমাদের যা যা জানতে হবে তা হলোঃ + - OOP + - OOP Design Pattern & Principles + - DSA + - Clean Code Architecture + - Problem Solving +- Testing + - Test Driven Development (আগে টেস্ট কোড লিখে এরপর মেইন কোড লিখবো) + - E2E (End to End) (হেডলেস ব্রাউজার ইউজ করে টেস্ট করা) +- Distribution + - DevOps (যে কাজগুলো অপারেশনাল রিলেটেড সেই কাজগুলো অটোমেট করা যায় এর মাধ্যমে) + - Continuous - যে কথাগুলোর সাথে কন্টিনিউ কথাটা আছে সেখানেই ডেভঅপ্স। নিচের ছবি দেখলে আরো ক্লিয়ার হবে। + devops + - Git and Github + - Linux, SSH - Secured Shell Homepage + - Circle CI, Travis CI, Jenkins (Integration server) + - Docker (For Single Service), Docker Compose (For Multiple Service) + - Docker Swarm, Kubernetes (Multiple Place এ থাকা অ্যাপ্লিকেশন এক জায়গা থেকে ম্যানেজ করার জন্য) + - Prometheus, Nagios (Continuous monitoring এর জন্য) + - Ansible + - Cloud Engineering - আমাদের একটা ক্লাউড দরকার যেখানে আমরা আমাদের অ্যাপ্লিকেশনকে ডেপ্লয় করতে পারি। এখানে অসংখ্য ডিজাইন ডিসিশন চলে আসে। যেমন + - VPC (Virtual Private Cloud) এর মাধ্যমে এই ক্লাউডের মধ্যে যে অ্যাপ্লিকেশন আছে তারাই শুধুমাত্র আমার ডাটাবেইজের এক্সেস নিতে পারবে এই ব্যবস্থা করে দেয়া যাবে। + - EC2 (Elastic Computing Cloud) Actual server যে আমার অ্যাপ্লিকেশনকে রান করবে + - S3 (Provide Storage Service) + - RDS (Relational Database Service) + - DynamoDB + - Route 53 + - CloudFront (CDN) + - SES (Simple Email Service) + - SNS (Simple Notification Service) + - SQS (Simple Queue Service) + - API Gateway + - Lambda + - Step Functions + - Cloud Watch + - Cloud Formation, Terraform, CDK + এগুলো ব্যবহার করার মাধ্যমে আমরা Non functionalities requirements প্রোভাইড করতে পারবো। +- Maintenance + +## Decision + +উপরে যা শিখলাম তার উপর ভিত্তি করে এবার আমাদের অ্যাপ্লিকেশনের ডিসিশন নিয়ে নিই। যেহেতু MVP হবে এবং বলা আছে কোনো হাই এভেইলেবিলিটি বা স্কেলেবিলিটির দরকার নেই তাই আমরা ক্লাউড ইঞ্জিনিয়ারিং পার্ট বাদ দিতে পারি। জাস্ট ৪/৮ জিবি র‍্যামের সার্ভার হলেই চলবে। ইনিশিয়ালি ডেভঅপ্সেরও প্রয়োজন নাই কারণ আমাদের দ্রুত একটা ওয়ার্কেবল প্রোটোটাইপ লাগবে। পরে অবশ্যই দরকার হবে। + +- যেহেতু MVP, সেহেতু Monolithic Architecture use করবো। +- যেহেতু ডিসিশন র‍্যানডম চেইঞ্জ হবে তাই আমরা ইউজ করবো NoSQL ডাটাবেইজ (MongoDB Atlas). +- ব্যাকএন্ডের জন্য আমাদের দুইটা প্রশ্ন করতে হবে নিজেদের। প্রথম প্রশ্ন আমাদের অ্যাপ্লিকেশনে হেভি কম্পিউটিং করতে হবে কিনা (ভিডিও প্রসেসিং / ইমেজ প্রসেসিং)? যদি না হয় তাহলে .Net, Java Spring boot, Go lang বাদ। দ্বিতীয় প্রশ্ন হলো আমার অ্যাপ্লিকেশন MVC, multipage কিনা? যদি হয় তাহলে NodeJS বাদ। আমরা সিলেক্ট করবো Django, Laravel বা Ruby on rails. যদি MVC না হয়, ডেটা ড্রাইভেন হয় এবং রিয়েল টাইম হয় তাহলে চোখ বন্ধ করে NodeJS. আমাদের অ্যাপ্লিকেশন ডেটা ড্রাইভেন তাই আমাদের অ্যাপ্লিকেশনের জন্য সিলেক্ট করবো NodeJS. কারণ সে কোনো রিকোয়েস্ট ব্লক করবে না। এক্ষেত্রে নোড জেএসের ফ্রেমওয়ার্ক হিসেবে Hapi, Express বা Fastify ব্যবহার করতে পারি। + + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/articles/application-requirements-and-landscape/Waterfall_model.png b/docs/Lectures/Fundamentals/01/Waterfall_model.png similarity index 100% rename from articles/application-requirements-and-landscape/Waterfall_model.png rename to docs/Lectures/Fundamentals/01/Waterfall_model.png diff --git a/articles/application-requirements-and-landscape/agile-model-of-se.png b/docs/Lectures/Fundamentals/01/agile-model-of-se.png similarity index 100% rename from articles/application-requirements-and-landscape/agile-model-of-se.png rename to docs/Lectures/Fundamentals/01/agile-model-of-se.png diff --git a/articles/application-requirements-and-landscape/devops.png b/docs/Lectures/Fundamentals/01/devops.png similarity index 100% rename from articles/application-requirements-and-landscape/devops.png rename to docs/Lectures/Fundamentals/01/devops.png diff --git a/resources/lecture-0/lecture0-diagram.jpg b/docs/Lectures/Fundamentals/01/lecture0-diagram.jpg similarity index 100% rename from resources/lecture-0/lecture0-diagram.jpg rename to docs/Lectures/Fundamentals/01/lecture0-diagram.jpg diff --git a/docs/Lectures/Fundamentals/01/resource.md b/docs/Lectures/Fundamentals/01/resource.md new file mode 100644 index 0000000..0479a89 --- /dev/null +++ b/docs/Lectures/Fundamentals/01/resource.md @@ -0,0 +1,7 @@ +# Resource + +# 0. Full-stack Army - Welcome | Decision-Making Video + +- **Road to a full stack developer** + + ![MERN-stack](lecture0-diagram.jpg) diff --git a/docs/Lectures/Fundamentals/02/Overview.md b/docs/Lectures/Fundamentals/02/Overview.md new file mode 100644 index 0000000..d92ca2f --- /dev/null +++ b/docs/Lectures/Fundamentals/02/Overview.md @@ -0,0 +1,158 @@ +## We Need Freedom, We have to Stop Technology War + +লাস্ট ক্লাসে আমরা যে সমস্ত টেকনোলজির নাম দেখেছিলাম তাতে অনেকেরই ভয় পাওয়া স্বাভাবিক যে এত এত জিনিস কিভাবে আমি শিখবো। আপনাদের ভয় পাওয়ার কোনো প্রয়োজন নেই। টেকনোলজি শেখার খুব ভাল উপায় হচ্ছে কোনোকিছুতে মাস্টার হওয়া যাবে না একবারেই। ধীরে ধীরে হতে হবে। ধরেন আপনি এইচটিএমএল দিয়ে শুরু করলেন। এখন আপনি এইচটিএমএলের একদম সমস্ত বিষয় জেনে তারপর সিএসএসে যেতে চান তাহলে আপনার আর যাওয়া হবে না। উপায় কী? উপায় হলো এইচটিএমএলের একটা বেসিক আন্ডারস্ট্যান্ডিং নেয়া। যেমন কিভাবে এইচটিএমএল স্ট্রাকচার লিখতে হয়, কিভাবে লে-আউট করতে হয়, কিভাবে টেক্সট, ইমেজ অ্যাড করতে হয় মানে জাস্ট বেসিকটা নিয়ে সিএসএসে জাম্প করা। আপনি সিএসএস শিখতে গেলে আল্টিমেটলি আপনার এইচটিএমএল লাগবেই। তখন যখন আপনার মনে হবে এইচটিএমএলের এই জিনিসটাতো আমি জানিনা তখন আপনি পিছনে এসে ওটা দেখে আবার সামনে যাবেন। এবার সিএসএসের যে সব শিখতে হবে তা নয়। দৈনন্দিন আপনার যা লাগবে তাই শিখে জাভাস্ক্রিপ্টে জাম্প করেন। কারণ জাভাস্ক্রিপ্ট শিখতে গেলে আপনাকে এইচটিএমএল সিএসএস লিখতেই হবে। সুতরাং কিছু বাদ গেলে তা শেখা যাবেই। চিন্তার কিছু নেই। + +আপনি কোনো জিনিস শিখতে গেলে যখন কোনো একটা টপিক বুঝবেন না তখন সেটা স্কিপ করে যান। একসময় গিয়ে দেখবেন যেটা বুঝেননি সেটা বুঝার জন্য যে ধরণের ব্রেইনস্টর্মিং দরকার তা হয়ে যাবে। এরপর একটু ব্যাকে এসে তা শিখে নিন। এভাবে হলে কোনো জিনিস শিখতে আপনার বেশি সময় লাগবে না। এবং ভয়ও লাগবে না। + +## আজকের এজেন্ডা + +এবার আমাদের আজকের এজেন্ডায় আসা যাক। আজকের এজেন্ডাগুলো হলোঃ + +- We need freedom, we need to stop technology war +- Why do we need programming language? +- Why different programming languages for client & server? +- Understanding programming paradigms. +- Programming is always same, but the language varies. + +## Why do we need programming language? + +একটা অ্যাপ্লিকেশনের জন্য যে যে স্টেপ লাগবে তা হলোঃ + +- Requirements +- Design +- Implementation + - UI/UX Design + - Web Design + - Frontend Development + - Backend Development + - Test Code +- Testing, Deployment +- Maintenance + +উপরোক্ত স্টেপগুলোর মধ্যে থেকে আমাদের Implementation স্টেপে মূলত প্রোগ্রামিং ল্যাঙ্গুয়েজ লাগছে। আরো স্পেসিফিকভাবে বলতে গেলে ফ্রন্টএন্ড এবং ব্যাকএন্ড ডেভেলপমেন্টে প্রোগ্রামিং ল্যাঙ্গুয়েজ লাগছে। এখন প্রশ্ন হলো কেন লাগছে। প্রোগ্রামিং ল্যাঙ্গুয়েজ দিয়ে আমরা কী করি? সহজ ভাষায় বলতে গেলে প্রোগ্রামিং ল্যাঙ্গুয়েজ একটা কাজই করে। সেটা হলো আমরা প্রোগ্রামিং ল্যাঙ্গুয়েজ দিয়ে কিছু কোড লিখি যা দিয়ে আমাদের কম্পিউটার বুঝতে পারে আমরা কি বুঝাতে চাইছি। প্রোগ্রামিং ল্যাঙ্গুয়েজের মাধ্যমে আমরা কোনো রিয়েল লাইফ প্রব্লেম কম্পিউটারকে বুঝাতে পারি। এই কাজ বাদে দুনিয়ার কোনো প্রোগ্রামিং ল্যাঙ্গুয়েজের আর কোনো কাজ নেই। যদিও প্রতিটা ল্যাঙ্গুয়েজ আসার পিছনে বিভিন্ন কারণ আছে। কিন্তু কাজ ঘুরেফিরে একটাই। প্রতিটা ল্যাঙ্গুয়েজ কোনো না কোনোভাবে আমার আর কম্পিউটারের মাঝে একটা ট্রান্সলেটর হিসেবে কাজ করছে। ধরুন, একজন আমেরিকান মানুষ, তার সাথে কমিউনিকেট করতে হবে। এখন চাইনিজরা চাইনিজ ট্রান্সলেটর আনবে, জার্মানরা জার্মান ট্রান্সলেটর, স্প্যানিশরা স্প্যানিশ ট্রান্সলেটর। কিন্তু উদ্দেশ্য একটাই। কম্পিউটাররূপী আমেরিকানের সাথে কমিউনিকেট করা। স্বাভাবিকভাবেই জার্মান, চাইনিজ বা স্প্যানিশদের ইংলিশ একসেন্ট তো আর আমেরিকান একসেন্টের মতো হবে না। ভাষাগত একটু পার্থক্য থাকবে। কিন্তু উদ্দেশ্য যেটা যে আমেরিকানের সাথে কমিউনিকেট করা সেটা সফল। + +## Why different programming languages for client & server? + +একটা বড় প্রশ্ন আছে। যদি সব ল্যাঙ্গুয়েজই কম্পিউটারের সাথে কমিউনিকেট করার জন্য হয় তাহলে এত ল্যাঙ্গুয়েজ কেন? এর উত্তর বিভিন্নভাবে দেয়া যায়। ধরেন আমরা বাংলাদেশী। এখন আমরা যখন ইংরেজিতে কিছু বুঝাতে চাইবো তখন আমরা আমাদের মধ্য থেকে একজনকে ট্রান্সলেটর হিসেবে নিয়োগ দিবো। এরকম ভারতীয়রা ভারতীয়দের, জার্মানরা জার্মানদের, চাইনিজরা চাইনিজদের নিয়োগ দিবে। প্রতিটা দেশ তাদের মতো করে তাদের প্রব্লেম সলভ করার জন্য তাদের মতো করে ট্রান্সলেটর নিয়োগ দিবে। সেরকম যখন মাইক্রোসফট একটা প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি করেছে তখন তারা তাদের প্রব্লেম সলভ করার জন্য প্রোগ্রামিং ল্যাঙ্গুয়েজটা তৈরি করেছে। যখন অ্যাপল একটা প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি করেছে তখন তারা তাদের প্রোডাক্টের সাথে খাপ খায় এমন একটা প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি করেছে। যখন ডেনিস রিচি ইউনিক্স অপারেটিং সিস্টেম তৈরি করতে গিয়ে আটকে গেলেন তখন তিনি ঐ সময়কার প্রব্লেম সলভ করার জন্য প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি করেছেন। এরপর সি যেসব প্রব্লেম সলভ করতে পারছিল না সেসব সলভ করার জন্য অবজেক্ট অরিয়েন্টেড ভিত্তিক ল্যাংগুয়েজ সি++ আসলো। সান মাইক্রোসিস্টেম যখন আসলো তখন তারা তাদের প্রব্লেম সলভ করার জন্য জাভা প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি করলো। তাহলে দেখা যাচ্ছে প্রতিটা কোম্পানি তাদের নিজস্ব প্রব্লেম সলভ করার জন্য বিভিন্ন প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি করেছে। + +আজকে জাভাস্ক্রিপ্ট নিয়ে এত তোলপাড়। এই জাভাস্ক্রিপ্ট মেইনলি আসার পিছনে কারণ ছিল ব্রাউজারের এইচটিএমএলে কিছু interactivity যোগ করা। এটাই ছিল আসল কারণ। জাভা যখন এসেছিলো তখন উদ্দেশ্য ছিল পোর্টেবল ডিভাইসে (এমবেডেড ডিভাইস, মিডিয়া প্লেয়ার, সিডি প্লেয়ার) প্রোগ্রাম রান করা। সি আসার উদ্দেশ্য অপারেটিং সিস্টেম ডিজাইন করা। সুতরাং দেখা যাচ্ছে একেকটা প্রোগ্রামিং ল্যাঙ্গুয়েজ একেকটা কারণে বাজারে এসেছে। তাই তাদের ফ্লেভারও ভিন্ন ভিন্ন। কিন্তু উদ্দেশ্য একটাই, প্রব্লেম সলভ করা। আমাদের মানুষের সাথে কম্পিউটারের কমিউনিকেশন তৈরি করা। + +পিএইচপি আসার একটা সুন্দর ইতিহাস আছে। যখন ওয়েব আসলো তখন ওয়েবসাইট সুন্দর করে তৈরি করার জন্য একটা স্ক্রিপ্টিং ল্যাঙ্গুয়েজের প্রয়োজন ছিল। যেটা এক্সিস্টিং কোনো ল্যাঙ্গুয়েজ সলভ করতে পারছিলো না। তখন ছিল ওয়েব ১.০। শুধু ডাটা পাস হবে। সেই উদ্দেশ্যে ক্রিয়েটর পিএইচপি তৈরি করেন। পরবর্তীতে তা যথেষ্ট জনপ্রিয়তা লাভ করে। + +প্রত্যেকটা ল্যাঙ্গুয়েজের একটা নির্দিষ্ট উদ্দেশ্য আছে। কে তৈরি করছে তার প্রয়োজন অনুসারে সামান্য পরিবর্তন হচ্ছে। কিন্তু ঐ যে বললাম উদ্দেশ্য সবার একই। প্রব্লেম সলভ করা। + +এখন আমাদের মেইন প্রশ্নে আসি। ডিফারেন্ট ল্যাঙ্গুয়েজ কে্ন প্রয়োজন ক্লায়েন্ট এবং সার্ভারের জন্য? + +প্রথম প্রশ্ন হলো প্রোগ্রামিং ল্যাঙ্গুয়েজ রান করে কোথায়? অবশ্যই একটা মেশিনে। কম্পাইলারে কোড কম্পাইল হওয়ার পর আল্টিমেটলি মেশিনে রান হয়। সেটা হতে পারে কম্পিউটার, সেটা হতে পারে টিভি, সেটা হতে পারে ফ্রিজ। মোটকথা যেখানে প্রসেসর আছে, কিছু র‍্যাম আছে, কিছু মেমোরি স্টোরেজ আছে যেখানে আমার কোডটা রাখা যাবে আর একটা কম্পাইলার ইনস্টল করা যাবে সেখানেই প্রোগ্রাম রান করানো যাবে। + +যদি তাই হবে তাহলে আমার ব্রাউজার বা ক্লায়েন্টের জন্য এক ধরণের ল্যাঙ্গুয়েজ আর সার্ভারের জন্য অন্যধরণের ল্যাঙ্গুয়েজ কেন? এই ওয়েবের জগতে ওয়েবসাইটের ফ্রন্টএন্ডের যে সেকশনটা আছে সেটার প্রোগ্রাম রান হবে একটা ব্রাউজারে। এখন ব্রাউজার তো কম্পিউটার না। ব্রাউজার হলো কম্পিউটারের মধ্যে থাকা ছোট একটা সফটওয়্যার। আপনি কম্পিউটারে চাইলে সি, সি++, জাভা, পাইথন ইত্যাদি যেকোনো কম্পাইলার ইনস্টল করতে পারবেন। কিন্তু ব্রাউজারে তো এতো কিছু সম্ভব না। সেজন্য ব্রাউজার বানানোর সময় জাভাস্ক্রিপ্ট নামক একটা ল্যাঙ্গুয়েজ সেখানে দিয়ে দেয়া হয়েছে। সুতরাং আমাদের জাভাস্ক্রিপ্ট শিখতে হবে ব্রাউজারের ইন্টের‍্যাক্টিভ ফিচারগুলো অ্যাড করার জন্য। আর কোনো ল্যাঙ্গুয়েজ এখানে কাজ করবে না। সুতরাং আমাকে ওয়েব অ্যাপ ডেভেলপ করতে হলে অবশ্যই জাভাস্ক্রিপ্ট শিখতেই হবে। যদি কাল এমন কোনো ব্রাউজার বাজারে চলে আসে যে তার কাজ হবে পাইথনে তাহলে আমাদের ফ্রন্টএন্ডের কাজ করার জন্য অবশ্যই পাইথন শিখতে হবে। কিছু করার নাই। কিছু মানুষ সিদ্ধান্ত নিয়েই নিয়েছে ব্রাউজারে জাভাস্ক্রিপ্ট ছাড়া আর কোনো ল্যাঙ্গুয়েজ তারা সহ্য করবে না। তাই আমাদের জাভাস্ক্রিপ্ট শেখার বিকল্প নেই ফ্রন্টএন্ডের জন্য। +এবার আসি সার্ভারের ব্যাপারে। এটাই হলো আল্টিমেট মেশিন। আমরা যে রিকোয়েস্টগুলো পাঠাই এটি তা সার্ভ করে, তাই এর নাম হচ্ছে সার্ভার। আমরা যদি বলি এই পেইজটা দাও বা এই ইমেজটা দাও বা এই পোস্ট বা ভিডিওটা দাও, সে সার্ভ করে দিলো। বেসিক্যালি এটা একটা কম্পিউটার। কম্পিউটারে যেকোনো প্রোগ্রামিং ল্যাঙ্গুয়েজ রান করতে পারে। + +তাহলে দেখা যাচ্ছে ব্রাউজার বা ক্লায়েন্টের জন্য আমরা জাভাস্ক্রিপ্টের বাইরে যেতে পারি না। কিন্তু যেহেতু সার্ভার একটি কম্পিউটার আর কম্পিউটারে যেকোনো ল্যাঙ্গুয়েজ রান করানো যায় সুতরাং আমরা সার্ভারের ল্যাঙ্গুয়েজ সিলেক্টের ব্যাপারে উন্মুক্ত। আমরা সি, সি++, জাভা, গো, পাইথন, এমনকি অ্যাসেম্বলি ল্যাঙ্গুয়েজ ব্যবহার করেও একটা সার্ভার সাইড অ্যাপ্লিকেশন বানিয়ে ফেলতে পারি। কিন্তু ডিফারেন্ট ল্যাঙ্গুয়েজের দিন ছিল ২০০৯ সাল পর্যন্ত। ২০০৯ সালের আগ পর্যন্ত জাভাস্ক্রিপ্টকে ব্রাউজারের বাইরে কেউ কল্পনাও করতে পারেনি। তাই একরকম বাধ্য হয়েই তখন ব্রাউজার বা ক্লায়েন্টের জন্য জাভাস্ক্রিপ্ট আর সার্ভারের জন্য পাইথন, পিএইচপি, রুবি ইত্যাদি ল্যাঙ্গুয়েজ শিখতে হতো। এজন্য আমাদের মাল্টিপল ল্যাঙ্গুয়েজের প্রয়োজন ছিল। +কিন্তু রায়ান ডেলকে ধন্যবাদ তিনি Nodejs ক্রিয়েট করার কারণে এখন আর ক্লায়েন্ট আর সার্ভারের জন্য মাল্টিপল ল্যাঙ্গুয়েজ শিখতে হয় না। এক জাভাস্ক্রিপ্ট শিখলেই ফ্রন্টএন্ডের কাজও করা যায়, আবার API বানানো যায়, ব্যাকএন্ড বানিয়ে ফেলা যায়। নোড জেএস একটা রেভ্যুলেশন। ডেভেলপমেন্ট জগতকে আরো সহজ করার পাশাপাশি জাভাস্ক্রিপ্টকে সার্ভার সাইড ল্যাঙ্গুয়েজ হিসেবে শক্ত অবস্থানে আজ পৌঁছে দেয়ার কৃতিত্ব নোড জেএস এবং অবশ্যই রায়ান ডেলের। + +তবে একটা ল্যাঙ্গুয়েজ দিয়ে দুইদিকেই কাজ করতে পারলেও সার্ভার সাইডের জন্য আমরা উন্মুক্ত। আমরা যেকোনো ল্যাঙ্গুয়েজ দিয়েই সার্ভার সাইড অ্যাপ্লিকেশন বানাতে পারি। + +এখন আমি ব্রাউজারের জায়গায় যদি মোবাইল নিয়ে আসি তাহলে কী হবে? মোবাইলের নরমালি আমরা দুইটা অপারেটিং সিস্টেমের সাথে পরিচিত। অ্যান্ড্রয়েড এবং আইওএস। আইওএস বানিয়েছে অ্যাপল। সে রেস্ট্রিক্টেড করে দিলো আমার অপারেটিং সিস্টেমে সুইফট (Swift) আর অবজেক্টিভ সি ছাড়া কোনো ল্যাঙ্গুয়েজ আমি ঢুকতে দিবো না। কিন্তু অ্যান্ড্রয়েডের কোনো ব্যারিয়ার নাই। সেখানে সি++, কটলিন, জাভা যেকোনো ল্যাঙ্গুয়েজ দিয়েই অ্যাপ বানানো যেতে পারে। শুধু এই তিন ধরণের ল্যাঙ্গুয়েজ দিয়েই যে অ্যান্ড্রয়েড অ্যাপ বানানো যায় তা নয়। আমরা যদি অপারেটিং সিস্টেম ভাংতে পারি, মানে অ্যান্ড্রয়েড ফোন রুট করতে পারি তাহলে পুরো সিস্টেমের এক্সেস মেশিনের কাছে চলে আসবে। আর এক্সেস আসার পরে যেহেতু মোবাইল একটা মেশিন, এর প্রসেসর, র‍্যাম, হার্ডডিস্ক সব আছে সুতরাং এখানে যেকোনো বাইনারি কোড রান করানো যাবে। এখন আমরা অ্যান্ড্রয়েডে লিনাক্স ইউজ করতে পারি, প্রোগ্রামিং করতে পারি কারণ এটা একটা মেশিন। এতে যেকোনো ল্যাঙ্গুয়েজ রান করানো যাবে। কিন্তু আইওএসের ক্ষেত্রে ওরা ব্যারিয়ার তৈরি করে রেখেছে, সেখানে কিছু করার নেই। + +**সারমর্মঃ** ক্লায়েন্টের জন্য শুধুই জাভাস্ক্রিপ্ট আর সার্ভারের জন্য জাভাস্ক্রিপ্টসহ যেকোনো ল্যাঙ্গুয়েজ। আর মোবাইলের ক্ষেত্রে আইওএসে সুইফট আর অবজেক্টিভ সি ছাড়া অন্য ল্যাঙ্গুয়েজ ব্যবহার করা যায় না, আর অ্যান্ড্রয়েডের ক্ষেত্রে কোনো ব্যারিয়ার নেই। + +## Understanding programming paradigms + +এই যে প্রোগ্রামিং এর যুদ্ধ তার মূলে হলো এই প্রোগ্রামিং প্যারাডাইম। এখন প্যারাডাইম মানে কী? বইয়ের ভাষায় বলতে গেলে অনেক কমপ্লেক্স হয়ে যাবে। সহজ ভাষায় বললে বলতে হয় এটা অনেকটা ক্যাটাগরির মতো। যেমন এই প্রোগ্রামিং ল্যাঙ্গুয়েজ এই ধরণের কাজ করবে, ঐ প্রোগ্রামিং ল্যাঙ্গুয়েজ ঐ ধরণের কাজ করবে। এভাবে সব ল্যাঙ্গুয়েজকে বিভিন্ন ক্যাটাগরির মধ্যে ফেলাকে প্রোগ্রামিং প্যারাডাইম বলে। + +আমদেরকে যখন প্রোগ্রামিং শেখানো হয় তখন প্রোগ্রামিং এর মতো করে শেখানো হয় না। হয় ল্যাঙ্গুয়েজ স্পেসিফিকভাবে। এখন অনেকদিন ধরে একটা ল্যাঙ্গুয়েজ শিখতে শিখতে তার প্রতি ভালবাসা থেকে বলেন বা অন্য ল্যাঙ্গুয়েজ শেখার প্রতি অনীহা থেকেই বলেন আমরা একটা ল্যাঙ্গুয়েজের উপর স্পেসিফিক হয়ে যাই। কিন্তু যদি abstractly আমাদের প্রোগ্রামিং শেখানো হতো তাহলে আর ল্যাঙ্গুয়েজ নিয়ে আমাদের ভাবতে হতো না। নিজেদের প্রয়োজনে যেকোনো ল্যাঙ্গুয়েজ আমরা সহজেই বুঝে নিয়ে ব্যবহার করতে পারতাম। এখন সেটার জন্য দরকার প্রোগ্রামিং ল্যাঙ্গুয়েজগুলো কি কি প্রব্লেম সলভ করে তার একটা শ্রেণীবিন্যাস করা। আর এক্ষেত্রে তা হলো প্রোগ্রামিং প্যারাডাইম। + +প্রোগ্রামিং এর মূলত দুইটা প্যারাডাইম আছে। যথাঃ ১. ইম্পেরেটিভ প্যারাডাইম ২. ডেক্লারেটিভ প্যারাডাইম +উইকিপিডিয়ার মতে ইম্পেরেটিভ প্যারাডাইম হলো, `in which the programmer instructs the machine how to change its state` এবং ডেক্লারেটিভ প্যারাডাইম হচ্ছে, `in which the programmer merely declares properties of the desired result, but not how to compute it` + +এখন একটু সহজ করে বুঝানো যাক। ধরেন আপনি কাউকে বললেন, ভাই আমি এই বইয়ের নামটা লিখে দিলাম। আপনি নীলক্ষেত থেকে কষ্ট করে বইটা কিনে আনেন। আমি শুধু বলেছি নীলক্ষেত থেকে বই কিনে আনতে। আমি বলিনি কিভাবে যাবে। সেটা যে যাবে তার উপর নির্ভর করবে। এটা হলো ডেক্লারেটিভ প্যারাডাইম। তাহলে এবার সংজ্ঞা থেকে বুঝুন যেখানে প্রোগ্রামার জাস্ট রেজাল্টটা আসার জন্য প্রোপার্টিজ ডিক্লেয়ার করবে, কিন্তু বলবে না কিভাবে সেটা করতে হবে। + +এবার আসা যাক ইম্পেরেটিভ প্যারাডাইমের কাছে। ধরেন আমি সেই বই কিনে আনতে বললাম কিন্তু প্রোপার ইনস্ট্রাকশন দিয়ে। ইনস্ট্রাকশনগুলো নিচে দেয়া হলোঃ + +- ধরলাম আমার বাসা ফার্মগেইট। বললাম আপনি ফার্মগেটের গোল চত্ত্বরে যাবেন। +- সেখানে ১০ নম্বর বাস পাবেন। সেটাতে উঠে ১০ টাকা ভাড়া দিবেন। +- এরপর নীলক্ষেতের ঠিক মোড়ে নামবেন। +- মোড়ে নামার পরে বামদিকে তাকাবেন। +- তাকানোর পর একটা লাইব্রেরি পাবেন। +- লাইব্রেরিতে ঢুকে যাবেন। +- ঢুকার পর দেখবেন অমুক বুক হাউজ নামে একটা হাউজ আছে +- সেই হাউজে ঢুকে এই বইয়ের দাম জিজ্ঞেস করবেন। +- বইয়ের দাম যদি ২০০ টাকার মধ্যে হয় তাহলে কিনবেন। +- যদি বেশি হয় পাশের দোকান যাবেন। +- গিয়ে দেখবেন বইটা আছে কিনা। +- যদি থাকে দাম জিজ্ঞেস করে যদি ২০০ টাকার মধ্যে হয় তাহলে কিনবেন। +- কিনে এরপর আবার নীলক্ষেত মোড়ে আসবেন। +- এসে ১০নাম্বার বাসে উঠে আগের মতো ফার্মগেইট চত্ত্বরে নামবেন। +- এরপর আমার বাসায় বইটা নিয়ে আসবেন। + এবার সংজ্ঞা থেকে বুঝুন ইম্পেরেটিভ প্যারাডাইমে প্রোগ্রামার মেশিনকে সুনির্দিষ্ট নির্দেশনা দিয়ে দিচ্ছে কিভাবে কী করতে হবে। এটাকে বলে ইম্পেরেটিভ প্যারাডাইম। + +### Imperative Paradigm + +ধরেন কেউ গ্রাম থেকে আসছে। সে ঢাকা চেনে না। তাকে স্টেপ বাই স্টেপ ইনস্ট্রাকশন দিয়ে যেতে হবে। মানে মেশিনের খুব কাছাকাছি ল্যাঙ্গুয়েজ যেগুলো সেগুলোকে ইনস্ট্রাক্টশন দিয়ে যেতে হবে লাইন বাই লাইন। সেক্ষেত্রে সি, সি++, জাভা এগুলোকে বলে লো লেভেল ল্যাঙ্গুয়েজ মানে মেশিনের খুব কাছাকাছি। জেনারেশনের দিক দিয়ে ভাবলে এগুলো সবই হাই লেভেল। কিন্তু যেহেতু এগুলো সব মেশিনের কাছাকাছি তাই এদের লো লেভেল ল্যাঙ্গুয়েজ হিসেবে গণ্য করার যায়। এগুলো উপর ভিত্তি করে ইম্পেরেটিভ প্যারাডাইমের ল্যাঙ্গুয়েজকে দুইভাগে ভাগ করা যায়- প্রোসিডিউরাল প্রোগ্রামিং এবং অবজেক্ট অরিয়েন্টেড প্রোগ্রামিং। + +**প্রোসিডিউরাল** - প্রোসিডিউরাল হলো কোনো গ্রুপ নাই, স্টেট নাই, কারো সাথে কারো সম্পর্ক নাই, একটা ফাইল ক্রিয়েট করে ইনস্ট্রাকশন লেখা শুরু করলাম টপ টু বটম। এটাই হচ্ছে প্রোসিডিউরাল। উইকিপিডিয়ার ভাষায় - `which groups instructions into procedures`। + +**অবজেক্ট অরিয়েন্টেড** - আমরা আগের মতোই একটা ফাইল ক্রিয়েট করবো। প্রোসিডিউরালে শুধু আমরা বিভিন্ন ভ্যারিয়েবল ডিক্লেয়ার করে বর্ণনা করেছি। কোনো লজিক্যাল গ্রুপ ছিল না। অবজেক্ট অরিয়েন্টেডেও আমরা বর্ণনা দিবো। কিন্তু লজিক্যাল গ্রুপে ভাগ করে। এখন আগের উদাহরণই যদি আমরা ধরি, তাহলে আমরা সেখানে বই কেনার জন্য একটা ক্লাস তৈরি করতে পারি। যেমন বই কোথায় পাওয়া যাবে, কোন বই, কিভাবে দাম করতে হবে ইত্যাদি। এরপর আমরা ট্রান্সপোর্টেশনের জন্য আমরা একটা ক্লাস বানাবো। কিভাবে ফার্মগেইট থেকে নীলক্ষেত যেতে হবে সেই ইনস্ট্রাকশন দিয়ে। এই যে দুইটা ভিন্ন ক্লাস বানালাম সেখানে একটা ক্লাসের অন্য ক্লাসের ইনফরমেশনের কোনো দরকার নেই। দুইটা ভিন্ন ভিন্ন গ্রুপ। অবজেক্ট অরিয়েন্টেডের সুবিধা হলো ক্লাসগুলো আমরা পুনরায় ব্যবহার করতে পারি। এখন ক এবং খ দুইজন ব্যক্তি। ক ঢাকায় থাকে, খ গ্রামে থাকে। এখন ক এর জন্য আমার ট্রান্সপোর্টেশনের ক্লাসটা সম্পূর্ণ ইনভ্যালিড। কারণ তাকে ঢাকা চেনাতে হচ্ছে না। তাকে শুধু বইয়ের ইনস্ট্রাকশন দিলেই হচ্ছে। সেক্ষেত্রে শুধু বইয়ের ক্লাস কাজে লাগবে। কিন্তু খ এর ক্ষেত্রে ট্রান্সপোর্টেশন এবং বই দুইটা ক্লাসই দরকার হবে। কারণ সে ঢাকা চেনে না। এক্ষেত্রে বইয়ের ক্লাস পুনরায় ইউজ করা যাচ্ছে। যদি প্রোসিডিউরাল হতো তাহলে ক এর জন্য কিছু কোড যুক্তিহীন হয়ে যাবে, কারণ তার জন্য ট্রান্সপোর্টেশনের কোড খাটবে না। আবার ধরা যাক গ ঢাকার বাইরে থাকেন, কিন্তু নীলক্ষেতে উনার পরিচিত মানুষ আছেন। তাহলে তার ক্ষেত্রে বই কেনার ইন্সট্রাকশন প্রয়োজন নাই। তার দরকার শুধু ট্রান্সপোর্টেশনের ইন্সট্রাকশন। তাহলে আপনারা বুঝতে পারছেন যে অবজেক্ট অরিয়েন্টেডের কি কি সুবিধা। + +এখন যদি আপনি অবজেক্ট অরিয়েন্টেড ভালভাবে বুঝে নেন, ল্যাঙ্গুয়েজ স্পেসিফিকভাবে না, তাহলে যেকোনো ল্যাঙ্গুয়েজেই আমরা অবজেক্ট অরিয়েন্টেড অ্যাপ্লাই করতে পারবে। ল্যাঙ্গুয়েজ কোনো বিষয় না। থিওরি বুঝলে আপনি যেকোনো ল্যাঙ্গুয়েজেই ঐ থিওরি অ্যাপ্লাই করতে পারবেন। শুধু সিনট্যাক্স শিখলেই হবে। + +### Declarative Paradigm + +ডেক্লারেটিভ প্রোগ্রামিং হলো আগে যেটা বলেছিলাম শুধু বই কিনে নিয়ে আসতে বলবো। এবার সে কিভাবে যাবে, কিভাবে দরাদরি করবে সেটা তার বিষয়। আমার তাতে কোনো মাথাব্যাথা নেই। আমার বই পেলেই হলো। + +ডেক্লারেটিভ প্যারাডাইমে আছে ফাংশনাল প্রোগ্রামিং, লজিক প্রোগ্রামিং, ম্যাথমেটিক্যাল প্রোগ্রামিং এবং রিয়্যাক্টিভ প্রোগ্রামিং। + +- লজিক প্রোগ্রামিংঃ সিস্টেম ডিজাইন রিলেটেড কোনো কাজ থাকলে এটা নিয়ে কাজ করা হয়। উইকিপিডিয়ার ভাষায়, `in which the desired result is declared as the answer to a question about a system of facts and rules` +- ম্যাথমেটিক্যাল প্রোগ্রামিংঃ এটা তারা ব্যবহার করে যারা অপটিমাইজ সল্যুশন আনার জন্য কাজ করেন। উইকিপিডিয়ার ভাষায়, `in which the desired result is declared as the solution of an optimization problem` + +ডেভেলপমেন্টের জন্য এই দুইটা জিনিস আমাদের কখনও দরকার হয় না। তার জন্য দরকার ফাংশনাল এবং কিছু কিছু ক্ষেত্রে রিয়্যাক্টিভ। + +- ফাংশনাল প্রোগ্রামিংঃ এটা অনেকটা অবজেক্ট অরিয়েন্টেডের মতোই। কিন্তু অবজেক্ট অরিয়েন্টেডে আমরা যেভাবে ডিটেইলস ইন্সট্রাকশন লিখে রাখি একটা গ্রুপের জন্য এখানে সেটা তা আমাদের জন্য কেউ না কেউ আগেই লিখে রেখেছে। আমাদের কাজ হচ্ছে জাস্ট কি করতে হবে সেটা কল করা। ডেক্লারেটিভ প্যারাডাইমের বেলায় একটা জিনিস খেয়াল রাখতে হবে আমাদের জন্য ইন্সট্রাকশন কেউ ইম্পেরেটিভ ওয়েতে লিখে রেখেছে আমরা শুধু তা ইউজ করব। সেজন্যই একে বলে ডেক্লারেটিভ। যেমন আমরা যখন `array.map()` বা `array.reduce()` ব্যবহার করি আমরা জানিনা এর পিছনের কোডটা কিভাবে লেখা আছে। আমাদের জানতেও হবে না। আমরা শুধু ডিক্লেয়ার করবো। উইকিপিডিয়ার ভাষায়, `in which the desired result is declared as the value of a series of function applications`। +- রিয়্যাক্টিভ প্রোগ্রামিংঃ এটা মূলত ব্যবহার করা হয় Asynchronous টাস্কের ক্ষেত্রে। উইকিপিডিয়ার ভাষায়, `in which the desired result is declared with data streams and the propagation of change`। এখানে যে data streams and the propagation of change এর কথা বলা হচ্ছে এগুলো সবই Asynchronous tasks. যেমন আপনি কোনো একটা বাটনে ক্লিক করলেন। সেটার জন্য একটা রেসপন্স পেলেন, সেই রেসপন্সের কারণে একটা জায়গার স্টেট চেইঞ্জ হলো। সেটার ফলে আরো দশটা জায়গায় চেইঞ্জ হলো। এটা হলো রিয়্যাক্টিভ প্রোগ্রামিং। svelte, vue, angular এরা সবই রিয়্যাক্টিভ ওয়েতে কাজ করে। কিন্তু react ভিন্ন ওয়েতে কাজ করে। সেটা অন্য কোনো জায়গায় আলোচনা করা হবে। রিয়্যাকশনের মানে হলো কিছু একটা ঘটার কারণে অন্য কোনো জায়গায় ডাটা চেইঞ্জ হচ্ছে। এটাই রিয়্যাক্টিভ প্রোগ্রামিং এর কনসেপ্ট। আর এটাও ডেক্লারেটিভ, কারণ আমার বিহাইন্ড দ্য সীন কি হচ্ছে জানার দরকার নেই। আমি শুধু ডিক্লেয়ার করবো। + +এইচটিএমএলও ডেক্লারেটিভ প্যারাডাইমের মধ্যে পড়ে। কারণ আমি `h1` ট্যাগ লেখার পর কোনোভাবেই বলে দিচ্ছি না তা কিভাবে কাজ করতে হবে। আমি জাস্ট ট্যাগটা লিখলাম। বাকিটা বিহাইন্ড দ্য সীন হয়ে যাচ্ছে। আরো ডিটেইলসের জন্য এই [লিংক](https://en.wikipedia.org/wiki/Declarative_programming#Subparadigms) ভিজিট করতে পারেন। + +
+ +এখন প্রশ্ন হলো জাভাস্ক্রিপ্ট, পাইথন এগুলো কোন ধরণের ল্যাঙ্গুয়েজ। এই বিষয় জানার আগে আমাদের একটু এই [আর্টিকেল](https://en.wikipedia.org/wiki/List_of_programming_languages_by_type) এ যেতে হবে। এটা একটা লিস্ট। ডিফারেন্ট প্রোগ্রামিং ল্যাঙ্গুয়েজ আছে ডিফারেন্ট টাইপের। যদিও এত ল্যাঙ্গুয়েজ আমাদের ডেভেলপমেন্টের জন্য লাগে না। তাও একবার চোখ বুলিয়ে নেয়া দরকার। কত টাইপের ল্যাঙ্গুয়েজ আছে, কোন ল্যাঙ্গুয়েজ কোন টাইপের মধ্যে পড়ে এসব। + +এখন এখানে খেয়াল করলে দেখা যাবে যে, সি, সি++, সি#, জাভাস্ক্রিপ্ট, পাইথন এগুলো সব ইম্পেরেটিভ ল্যাঙ্গুয়েজের মধ্যে দেয়া আছে। + +আবার এগুলো ফাংশনাল প্রোগ্রামিং এর মধ্যে আছে। ঘটনাটা পুরোই কনফিউজিং। + +ফাংশনাল প্রোগ্রামিং ল্যাঙ্গুয়েজ দুই প্রকার। পিওর এবং ইম্পিওর। এ সমস্ত ল্যাঙ্গুয়েজ ইম্পিওর ফাংশনাল প্রোগ্রামিং ল্যাঙ্গুয়েজের মধ্যে আছে। আগে আমাদের বুঝতে হবে তাহলে পিওর এবং ইম্পিওর ফাংশনাল প্রোগ্রামিং ল্যাঙ্গুয়েজ বলতে কী বোঝায়? + +- ইম্পিওর ফাংশনাল ল্যাঙ্গুয়েজঃ এসব ল্যাঙ্গুয়েজ চাইলে আমরা ফাংশনাল, অবজেক্ট অরিয়েন্টেড, রিয়্যাক্টিভ যেকোনো ওয়েতে ব্যবহার করতে পারি। আমাদের যা ইচ্ছা আমরা সেভাবে ব্যবহার করবো। এজন্য এগুলো ইম্পিওর ফাংশনাল ল্যাঙ্গুয়েজ। মানে আমরা ফাংশনালের বাইরেও অন্যভাবে এদের ইউজ করতে পারি +- পিওর ফাংশনাল ল্যাঙ্গুয়েজঃ এগুলো পুরোপুরিই ফাংশনাল। + +তার মানে আমাদের কোন ল্যাঙ্গুয়েজে এগুলো কিভাবে অ্যাপ্লাই করবো তা নিয়ে ভাবার দরকার নাই। আমাদের দরকার শুধু এগুলো কিভাবে কাজ করে তার কনসেপ্ট এবং যেকোনো একটা ল্যাঙ্গুয়েজে এগুলোর ইমপ্লিমেন্টেশন। যদি তা পারি তাহলে যেকোনো ল্যাঙ্গুয়েজে গিয়ে আমরা এগুলো অ্যাপ্লাই করতে পারবো। জাস্ট শুধু সিনট্যাক্স শিখলেই হবে। তবে আপনি ৫ দিন ধরে গোল্যাং দেখে, ৫ বছর ধরে যিনি গোল্যাং নিয়ে কাজ করছে তার মতো করতে পারবেন না। কিন্তু প্রব্লেম সলভ করতে পারবেন ডেফিনিটলি। + +এখন এই প্রোগ্রামিং প্যারাডাইম থেকে আমরা দেখলাম মূলত ৩ ধরণের প্রব্লেম সলভ করার জন্য বিভিন্ন ল্যাঙ্গুয়েজ এসেছে। যখন কিছুই ছিল না তখন প্রোসিডিউরাল ওয়েতে কোড লেখার জন্য আসলো সি। এরপর যখন কোড রিইউজের দরকার পড়লো, এন্টারপ্রাইজ লেভেলের অ্যাপ্লিকেশন তৈরির জন্য তখন আসলো জাভা, সি++ বা সহজ ভাষায় অবজেক্ট অরিয়েন্টেড প্রোগ্রামিং। এটা আমাদের অনেক প্রব্লেম সলভ করছে। আজকের দুনিয়ায় আমরা যেস বড় বড় অ্যাপ্লিকেশন দেখতে পারি এগুলো সবই হচ্ছে OOP এর ফল। এখন যেহেতু OOP ইম্পেরেটিভ প্যারাডাইম, তাই আমাদের প্রতিটা কোড লিখে লিখে ইন্সট্রাকশন দিতে হয়। সেক্ষেত্রে অনেক কোড লিখতে হয়। এই সমস্যার সমাধান করতে এসেছে ডেক্লারেটিভ প্যারাডাইমের ল্যাঙ্গুয়েজগুলো। যদিও কেউ না কেউ আমাদের জন্য লিখে রেখেছে, কিন্তু আমরা শুধু কমান্ড দিলেই কাজ হয়ে যাচ্ছে অনেক কম কোড লিখে। তাই এখন পাইথন, জাভাস্ক্রিপ্টের মতো ল্যাঙ্গুয়েজগুলো খুবই জনপ্রিয়। + +এখন বুঝলাম যে ৩ ধরণের প্রব্লেম সলভ করার জন্য ল্যাঙ্গুয়েজগুলো আসছে। তাহলে ৩টা ল্যাঙ্গুয়েজ থাকলেই তো হতো। এত এত ল্যাঙ্গুয়েজ কেন? সি এর সিনট্যাক্সের সাথে তো সি++ পুরোই মিলে, জাভা, জাভাস্ক্রিপ্টের কিছু কিছু মিলে। তাহলে কি এমন দরকার পড়লো যে আমাদের নতুন ল্যাঙ্গুয়েজ ক্রিয়েট করতে হলো? এই জায়গায় আমরা বিগিনাররা একটা ভুল করে ফেলি কারণ আমরা সিনট্যাক্স দিয়ে একটা ল্যাঙ্গুয়েজ বিচার করি। সিনট্যাক্স হলো আপনার কম্পিউটারের কীবোর্ডের মতো। এখন কোন কীবোর্ডে লাইট জ্বলে, কোনোটাতে জ্বলে না, কোনোটা মেকানিক্যাল, কোনোটা সেমিমেকানিক্যাল, কোনোটাতে নামপ্যাড আছে, কোনোটাতে নেই, কোনোটাতে মিডিয়া প্লেয়ার বাটন আছে ইত্যাদি। কিন্তু কীবোর্ডের কাজটা কিন্তু সেইম। নরমাল কীবোর্ড যেগুলো সেগুলো দেখলে দেখা যাবে একই রকম। শুধু কিছু ফিচার কম বেশি। তাহলে একটা কীবোর্ড দিলেই তো হয়ে যেতো। বাজারে এত কীবোর্ড কেন? সেরকম সিনট্যাক্স হচ্ছে অনেকটা কীবোর্ডের কী এর মতো। কিছু কমবেশি থাকতে পারে কীবোর্ড ভেদে, কিন্তু ম্যাক্সিমাম কী সেইম, তার ফাংশনালিটিজও সেইম। সেরকম সিনট্যাক্সও ল্যাঙ্গুয়েজ ভেদে মিল থাকতে পারে। মূল বিষয় হচ্ছে ইমপ্লিমেন্টেশন। আমি কিভাবে ইমপ্লিমেন্ট করছি কোনো ল্যাঙ্গুয়েজ। ইমপ্লিমেন্ট বলতে বুঝাচ্ছে এই ল্যাঙ্গুয়েজটা যারা তৈরি করেছে তারা কি ভেবে তৈরি করেছে। তাদের কম্পাইলার তারা কিভাবে ডিজাইন করেছে। এটা হচ্ছে ব্যাপার। কম্পাইলার অনেকভাবে ডিজাইন করা যায়। এই কম্পাইলারের ডিজাইন এর উপর ডিপেন্ড করে আমার প্রোগ্রাম কত কম সময়ে রান করবে। এই এক্সিকিউশন টাইম, মেমোরি, গারবেজ কালেকশন মানে কপ্লাইলার আর্কিটেকচারের উপর ভিত্তি করেই এই প্রোগ্রামিং ল্যাঙ্গুয়েজগুলো চেইঞ্জ হয়। আরেকটা বিষয় আছে সেটা হলো কনকারেন্সি। কনকারেন্সি মানে হলো ধরেন আপনার কম্পিউটারে ৮টা কোর আছে। আপনি সেই ৮টি কোর ব্যবহার করেই একই সময়ে কোড রান করাতে চাচ্ছেন। এটাই কনকারেন্সি। আগে কম্পিউটার প্রসেসর ছিল সিঙ্গেল কোর। এখন মাল্টিকোর। ৩০ বছর আগের কম্পিউটারের প্রসেসিং পাওয়ার আর আজকের কম্পিউটারের প্রসেসিং পাওয়ার তো এক না। সেক্ষেত্রে কম্পাইলার বারবার চেইঞ্জ করতে হয়েছে, আপগ্রেড করতে হয়েছে, ক্ষেত্রবিশেষে নতুন ল্যাঙ্গুয়েজ তৈরি করতে হয়েছে। যেমন সি কিন্তু গারবেজ কালেক্টর না। গারবেজ কালেক্টর বলতে বুঝায় আমরা যে ভ্যারিয়েবলের মধ্যে ডেটা রাখলাম, এ্যারে তৈরি করলাম, মেমোরিতে কিছু স্টোর করলাম, এগুলো নির্দিষ্ট সময় পরপর ক্লিন করতে হয়। সি সেটা পারে না। সেজন্য আমাদের নতুন করে মেমোরি কিভাবে কাজ করছে সেটা শিখতে হচ্ছে, সেভাবে মেমোরি ক্লিন করতে হচ্ছে। এরপর যখন আমরা জাভাতে আসলাম সেখানে কিন্তু আমাদের আর গারবেজ কালেক্ট করতে হচ্ছে না। সে অটোমেটিক গারবেজ কালেক্ট করে ক্লিন করে ফেলে। সুতরাং অনেক কিছুর উপর ভিত্তি করে প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি হয়, তেমনি কম্পাইলার ডিজাইনের উপর ভিত্তি করে ভিন্ন ভিন্ন প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি হয়েছে। একেকটা নির্দিষ্ট কাজের জন্য একেকটা প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি হয়েছে। কম্পাইলারের উপর ভিত্তি করে মূলত ৩ ধরণের প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি হয়েছে। এরা হলো কম্পাইল্ড, ইন্টারপ্রেটেড, জাস্ট ইন টাইম (JIT) + +- কম্পাইল্ডঃ কম্পাইল্ড হলো আমি যে কোড লিখেছি সেটা পিওর মেশিন কোডে রূপান্তরিত হবে। মেশিন কোড বলতে বুঝানো হচ্ছে বাইনারি কোড। কম্পাইল্ড ল্যাঙ্গুয়েজগুলো বেশি ফাস্ট হয়। সি, সি++ এরা হলো কম্পাইল্ড। জাভা এক হিসাবে কম্পাইল্ড। কিন্তু পিওর কম্পাইল্ড না। কারণ মাঝখানে জেভিএম নামে একটা ভার্চুয়াল মেশিন আছে যেটাতে জাভা রান হয়। তাই জাভা সি++ এর চেয়ে অনেক স্লো। সি এর থেকে আরো স্লো। কম্পাইল্ড ল্যাঙ্গুয়েজের স্টেপ ৩টা। প্রথমে আপনি কোড লিখবেন, এরপর আপনি বিল্ড করবেন exe বা class ফাইলে, এরপর আপনি রান করবেন। এতে আপনার কম্পাইলে টাইম লাগবে কিন্তু এক্সিকিউশনে খুব কম টাইম লাগছে। তাই প্রোগ্রাম অনেক দ্রুত রান হয়। আরেকটা সুবিধা হলো কম্পাইল করার সময় যদি আপনার কোডে কোনো ভুল থাকে সে আপনাকে ধরিয়ে দেবে। এটাকে বলে কম্পাইল টাইম এরর। এগুলোতে বুট করতে টাইম বেশি লাগে, যেহেতু একটা ইন্টারমিডিয়েট প্রসেস আছে কম্পাইল করতে, কিন্তু এক্সিকিউশনে টাইম কম লাগে। +- ইন্টারপ্রেটেডঃ এর কাজ হলো এ শুরুতেই কোড রান করে দিবে। এরপর যখন যেটা লাগবে সেটা সেভাবে রীড করে সে অনুযায়ী এক্সিকিউট করবে। যেমন পাইথন। ধরেন আপনি পাইথন কোড লিখলেন, রান বাটনে ক্লিক করলেন, সে লাইন বাই লাইন পড়বে না। তাই পুরো কোডের কোথাও যদি কোথাও ভুল থাকে সে রান করে ফেলবে, সে ধরতে পারবে না কারণ সে তো কম্পাইলই করছে না। যখন দরকার তখন সে সেই কোড পড়ে সেটা কম্পাইল করে মেশিনকে বুঝিয়ে দী। তাই এগুলোতে রানটাইম এরর বেশি পাওয়া যায়। এ সমস্ত ল্যাঙ্গুয়েজের অসুবিধা হলো এরা একটা কোড বারবার কম্পাইল করবে। তাই আমার এক্সিকিউশন টাইম অনেক বেশি লাগবে। সে কারণে ইন্টারপ্রেটেড ল্যাঙ্গুয়েজগুলো অনেক স্লো হয় কম্পাইল্ড ল্যাঙ্গুয়েজের তুলনায়। এগুলোতে বুট করতে টাইম কম লাগে, কিন্তু এক্সিকিউশনে টাইম বেশি লাগে। +- JIT: এক্ষেত্রে ফাইলটা ইনস্ট্যান্টলি রীড করে ফেলবে। এরপর যখন যা দরকার হবে তখন সে ইন্টারপ্রেট করবে না, সরাসরি কম্পাইল করে মেশিন কোডে রূপান্তর করে ফেলবে। তাহলে কোডগুলো থেকে যাচ্ছে, বারবার আর কম্পাইল করতে হচ্ছে না। আমি রিইউজ করতে পারছি। এটাই বেসিক কনসেপ্ট। + +জাভাস্ক্রিপ্ট একটা সময় ইন্টারপ্রেটেড ছিল। জাভাস্ক্রিপ্টের এই রেভ্যুলিউশনের জন্য দুইজন লোককে ক্রেডিট দিতে হয়। একজন হলেন সুন্দর পিচাই। তিনি যদি v8 engine এবং গুগল ক্রোম তৈরির কথা না ভাবতেন তাহলে আজকে জাভাস্ক্রিপ্টের এই জয়জোয়ার আসতো না। জাভাস্ক্রিপ্টকে ইন্টারপ্রেটেড থেকে JIT কম্পাইলারে নিয়ে আসার পিছনে কৃতিত্ব হচ্ছে গুগল এবং ঐ সময় তার লীডে থাকা সুন্দর পিচাই। দ্বিতীয় জন হলেন রায়ান ডেল। ঐ v8 এর উপর ভিত্তি করে তিনি জাভাস্ক্রিপ্টকে ব্রাউজারের বাইরে নিয়ে আসেন। এই JIT কম্পাইলারের কারণে জাভাস্ক্রিপ্টের ইতিহাস পালটে গেছে। কারণ একই ল্যাঙ্গুয়েজ যদি আপনি দুই জায়গায় ব্যবহার করতে চান তাহলে সেই ল্যাঙ্গুয়েজকে অনেক দ্রুত এক্সিকিউট হতে হবে। আগে সেটা জাভাস্ক্রিপ্টের ছিল না। কিন্তু v8 ইঞ্জিন আসার পর তা সম্ভব হয়েছে। এই সুযোগ আসায় জাভাস্ক্রিপ্ট দিয়ে অনেক বড় অ্যাপ্লিকেশন তৈরি করা সম্ভব হয়েছে। + +গো ল্যাং এর ইতিহাস যদি আমরা দেখি, সি এর কো-অথর কেন থম্পসন চিন্তা করলেন বড় অ্যাপ্লিকেশন তৈরির জন্য সি এর সিনট্যাক্সগুলো খুব ভাল না। তাই সি খুব সুবিধা করতে পারবে না। তাই আমাদের সি এর মতো সেইম পারফরম্যান্স থাকতে হবে, কিন্তু সহজে কোড করা যায়, গারবেজ কালেক্ট করা যায়, কনকারেন্সির বিষয়বস্তু সহজ এরকম একটা ল্যাঙ্গুয়েজ আনতে হবে। গো নামটা এসেছে গো রুটিন থেকে। গো রুটিনে কনকারেন্সি এত সহজ তা কল্পনা করা যায় না। সবদিক বিবেচনা করে তিনি গো নামক একটি লো লেভেল ল্যাঙ্গুয়েজ তৈরি করলেন যা আমাদের কাছে গো ল্যাং নামে পরিচিত। এটা একটা গেইম চেইঞ্জার। কারণ আমরা পাওয়ার পাচ্ছি সি এর মতো, কিন্তু কোড লিখছি পাইথনের মতো সহজ। + +তাহলে দেখেন যে সি জানে সে গো তে এসে কি আটকে যাওয়ার কথা? এত এত ল্যাঙ্গুয়েজ আসার পিছনে কারণ হচ্ছে কোনো একটা প্রব্লেম আমি এই ল্যাঙ্গুয়েজ দিয়ে সলভ করতে পারছি না সেজন্য নতুন একটা ল্যাঙ্গুয়েজ এসেছে। সি অনেক বছর আগের। তখন সেভাবে প্রব্লেম সলভ হতো। কিন্তু এখনকার দ্রুত দুনিয়ায় খাপ খাওয়াতে গো ল্যাং এর আবির্ভাব। মেইন রিজন হচ্ছে এটাই। কেউ কারো প্রতিদ্বন্ধী না। প্রত্যেকেই প্রত্যেকের উত্তরসূরী বা পূর্বসূরী। + +এমন ডার্টের কথা বলি। সব ল্যাঙ্গুয়েজের বেস্ট ফিচার্স নিয়ে তারা একটা ল্যাঙ্গুয়েজ তৈরি করেছে সহজে অ্যাপ্লিকেশন ডেভেলপমেন্টের জন্য যার নাম দিয়েছে ডার্ট। + +আরেকটা প্রশ্ন আসতে পারে এত আলোচনার পর। নতুন ল্যাঙ্গুয়েজ সৃষ্টি না করে কি বর্তমান ল্যাঙ্গুয়েজকে আপগ্রেড করা যেতো না? অবশ্যই যেতো, এবং করা হয়ও। ধরেন আপনি ৩০ বছর আগে ১৫ তলা একটা বিল্ডিং তৈরি করলেন। এখন আপনি কালার চেইঞ্জ করা, দরজা, জানালা চেইঞ্জ করা ছাড়া বর্তমান জমানার বিল্ডিং এ কী কনভার্ট করতে পারবেন? সম্ভব না। কারণ ৩০ বছর আগে সেটা তৈরি হয়েছিল তখনকার আর্কিটেকচার দিয়ে। এখন সেই পুরনো আর্কিটেকচার ভেঙেচুরে নতুন আর্কিটেকচারের বিল্ডিং বানানো সম্ভব না। যেমন নোড জেএস অল্প কিছদিন হলো এসেছে, তার আর্কিটেকচারই ভাঙতে পারলো না, ডেনো আনতে হলো। এটাই হলো মূল ব্যাপার। + +## শেষ কথা + +আমাদেরকে শিখতে হবে থিওরি। অবজেক্ট অরিয়েন্টেড, ফাংশনাল প্রোগ্রামিং, গারবেজ কালেকশন, কনকারেন্সি এসব আমরা যখন জানবো এবং যেকোনো একটা ল্যাঙ্গুয়েজ দিয়ে ইমপ্লিমেন্ট করতে পারবো অন্য যেকোনো ল্যাঙ্গুয়েজে গিয়ে আমাদের কষ্ট করতে হবে না। +আমরা যেকোনো কিছু শিখতে গেলে ল্যাঙ্গুয়েজ স্পেসিফিক হবো না, ফ্রেমওয়ার্ক স্পেসিফিক হবো না। আমরা চেষ্টা করবো abstractly তার কোর নলেজ শেখার জন্য। এরপর আমরা চেষ্টা করবো যে ল্যাঙ্গুয়েজে আছি তাতে কিভাবে অ্যাপ্লাই করবো। + + + +## Author + +- [Aditya Chakraborty](https://github.com/adityackr) diff --git a/docs/Lectures/Fundamentals/02/resource.md b/docs/Lectures/Fundamentals/02/resource.md new file mode 100644 index 0000000..e886722 --- /dev/null +++ b/docs/Lectures/Fundamentals/02/resource.md @@ -0,0 +1,21 @@ +# Resource +## Lecture 2 - We Need Freedom, We have to Stop Technology War + +**Today’s Agenda:** + +- We need freedom, we need to stop technology war +- Why do we need programming language? +- Why different programming languages for client & server? +- Understand programming paradigms. +- Programming is always same, but the language varies. + +**Important Links:** + +- [Programming paradigm - Wikipedia](https://en.wikipedia.org/wiki/Programming_paradigm) +- [List of programming languages by type - Wikipedia](https://en.wikipedia.org/wiki/List_of_programming_languages_by_type) +- [Imperative programming - Wikipedia](https://en.wikipedia.org/wiki/Imperative_programming) +- [Declarative programming - Wikipedia](https://en.wikipedia.org/wiki/Declarative_programming) +- [Functional programming - Wikipedia](https://en.wikipedia.org/wiki/Functional_programming) +- [Object-oriented programming - Wikipedia](https://en.wikipedia.org/wiki/Object-oriented_programming) + + diff --git a/docs/Lectures/Fundamentals/03/Overview.md b/docs/Lectures/Fundamentals/03/Overview.md new file mode 100644 index 0000000..fbed441 --- /dev/null +++ b/docs/Lectures/Fundamentals/03/Overview.md @@ -0,0 +1,157 @@ + +## Lecture 3 - Programming Language Foundation - A Bigger Landscape + +আজকের লেকচারটা আগের দুইটার তুলনায় অনেক গুরুত্বপূর্ণ হতে যাচ্ছে। কারণ আজ আমরা আমাদের অস্ত্র রেডি করতে যাচ্ছি। ডেভেলপমেন্টে আমাদের অস্ত্র কি হতে পারে? একটু ভাবুন তো। অবশ্যই ল্যাঙ্গুয়েজ। গত ক্লাসে সব প্রোগ্রামিং ল্যাঙ্গুয়েজ নিয়ে একটা আলোচনা করা হয়েছিল। আমরা বুঝতে পেরেছিলাম যে কোনো ল্যাঙ্গুয়েজ কেউ কারো শত্রু না। আজকে আমরা আরেকটা বড় ল্যান্ডস্কেপ নিয়ে আলোচনা করবো। এই ল্যান্ডস্কেপ যদি মাথায় বসিয়ে ফেলা যায় তাহলে যেকোনো ল্যাঙ্গুয়েজ খুব সহজ মনে হবে। + +যেকোনো কিছু শিখতে গেলে আগে পুরোটা ম্যাপ করে ফেলে এরপর অল্প অল্প করে শেখা উচিৎ। মানে আপনি যা শিখতে চাইছেন তা সেক্টরে সেক্টরে ক্লাসিফাই করে ফেলুন। এটা যদি করতে পারেন তাহলে আপনার জন্য যেকোনো কিছু শেখা সহজ হয়ে যাবে। + +আজ আমাদের কাজ হলো কিভাবে সহজে নতুন নতুন টেকনোলজি শেখা যায়। আমাদের আজকের এজেন্ডা হলো: + +- Programming Language Landscaping +- Visualize Programming Syntax + +## Programming Language Landscaping + +এই টপিক বুঝার জন্য আমাদের নিচের ডায়াগ্রামটা বুঝতে হবে। + +![Programming Language Landscaping](./Programming%20Language%20Landscape.png) + +প্রোগ্রামিং ল্যাঙ্গুয়েজের কি কি জিনিস আছে? এই প্রশ্নটা যদি করা হয় আর সব উত্তর যদি লিস্ট করা হয় তাহলে এর শুরু এবং শেষ খুঁজে পাওয়া যাবে না। সেক্ষেত্রে প্রতিটা জিনিস যদি আমি ক্যাটাগরিতে ভাগ করে ফেলতে পারি তাহলে আমাদের জন্য রোডম্যাপ তৈরি করাটা সহজ হয়ে যাবে। একটা প্রোগ্রামিং ল্যাঙ্গুয়েজের কোর কম্পোনেন্ট মূলত ৩টি। কোর কম্পোনেন্ট মানে হলো যা ছাড়া প্রোগ্রামিং ল্যাঙ্গুয়েজ কল্পনা করা যায় না। এই ৩টি কম্পোনেট হলো: + +- **ইনপুটঃ** ইনপুট অনেকভাবে দেয়া যায়। আমি কীবোর্ডে কিছু লিখছি সেটা ইনপুট, ফর্ম সাবমিট ইনপুট, ব্রাউজারে কিছু সার্চ করা ইনপুট। প্রোগ্রামিং ল্যাঙ্গুয়েজে অসংখ্য ইনপুট থাকতে পারে। কিন্তু আমরা যখন ল্যাঙ্গুয়েজ শেখা শুরু করবো আমাদের বাছাই করে নিতে হবে স্ট্যান্ডার্ড ইনপুট কোনটা। এর মানে হলো আমাদের টার্মিনাল ওপেন থাকবে, আমরা কীবোর্ড দিয়ে ইনপুট দিবো। একেই বলে স্ট্যান্ডার্ড ইনপুট। + +- **প্রসেসিংঃ** ইনপুট দেয়ার পর কোনো না কোনোভাবে সেটা প্রসেসিং করবে। এই কাজটা অনেক বড়। এই প্রসেসিংকে আবার দুই ভাগে ভাগ করা যায়ঃ + + - **ফাউন্ডেশনালঃ** একটা অ্যাপ্লিকেশনের মেইন প্রব্লেম সলভ করার জন্য যা যা লাগে তাদের এই ক্যাটাগরিতে ফেলা হয়। এখানে কিছু গুরুত্বপূর্ন টপিক আছে। + + - **ভ্যারিয়েবলঃ** একটা প্রোগ্রামিং ল্যাঙ্গুয়েজের অন্যতম গুরুত্বপূর্ণ বিষয় হলো ভ্যারিয়েবল। এটাকে আমরা তেমন একটা পাত্তা দিই না, কিন্তু এটা খুবই গুরুত্বপূর্ণ কোথায় ভ্যারিয়েবল নিতে হবে কোথায় নিতে হবে না। কোনো অপ্রয়োজনীয় ভ্যারিয়েবল নিলে হবে না, কারণ মেমোরি লিক হওয়ার চান্স থাকে। তাই এই টপিকটা বুঝার ব্যাপার আছে। + + - **স্টেটমেন্ট / এক্সপ্রেশনঃ** + + - **অপারেটরঃ** + + - **লজিকঃ** এর মধ্যে লজিক্যাল অপারেশন্স বা লজিক বিল্ডিং যেমন কন্ডিশন, লুপ এসব কিছুই চলে যায়। + + - **ফাংশনঃ** কোড পুনরায় ব্যবহার করার জন্য এটা খুবই অসাধারণ কম্পোনেন্ট। এই বিষয়ে আমরা বিস্তারিত জানবো। + + - **অ্যারে, কী-ভ্যালু পেয়ার ডাটা স্ট্রাকচারঃ** অনেক ধরণের ডাটা স্ট্রাকচার আছে। কিন্তু সবগুলো প্রব্লেম সলভ করতে লাগে না। কিন্তু দুইটা ডাটা স্ট্রাকচার আছে যেগুলো ছাড়া প্রব্লেম সলভ কল্পনাই করা যায় না। প্রতিটা প্রোগ্রামিং ল্যাঙ্গুয়েজ এই দুইটা ডাটা স্ট্রাকচার বাই ডিফল্ট দিয়ে দেয়। বাকিগুলো অনেক কমপ্লেক্স, সেটা আমাদের প্রয়োজনে আমরা ইমপ্লিমেন্ট করে থাকি। এই দুইটা ডিএসের মধ্যে একটা হলো অ্যারে। দ্য মোস্ট ইগ্নোরড ডাটা স্ট্রাকচার। অ্যারে যে একটা ডাটা স্ট্রাকচার এটা আমরা অনেকেই জানিনা। আপনি যদি [Array Data Structure - GeeksForGeeks](https://www.geeksforgeeks.org/array-data-structure/) লিংকে যান, তাহলে দেখবেন এক অ্যারে নিয়ে কি পরিমাণ অ্যালগরিদম আছে। এগুলো আমাদের জানা লাগবে। আরেকটা ডাটা স্ট্রাকচার যেটা হলো কী-ভ্যালু পেয়ার। অ্যারে এবং কী-ভ্যালু পেয়ার একে অন্যের পরিপূরক। অ্যারেতে আমরা নাম দিতে পারি না, ইনডেক্স ধরে ধরে কাজ করতে হয়। কিন্তু কী-ভ্যালু পেয়ারে আমরা ডেটার নামকরণ করতে পারি। এটা জাভাস্ক্রিপ্টে বলে অবজেক্ট, সি তে বলে স্ট্রাকচার, পাইথনে বলে ডিকশনারি, ডার্টে বলে ম্যাপ, জাভাতে একটু ডিফারেন্ট যেহেতু অবজেক্ট অরিয়েন্টেড ল্যাঙ্গুয়েজে তাই ক্লাস তৈরি করে দুইটা কী আর ভ্যালু দিয়ে দিলেই কী-ভ্যালু পেয়ার হয়ে যাচ্ছে। এদের মধ্যে সবচেয়ে শক্তিশালী হলো জাভাস্ক্রিপ্টের অবজেক্ট। সব ল্যাঙ্গুয়েজে কী-ভ্যালু পেয়ারের মধ্যে কিছু কিছু ভিন্নতা আছে। কিন্তু যেটা মূল ব্যাপার, সেটা হলো কী এবং ভ্যালু থাকবে। + + এই যে ছয়টা কম্পোনেন্ট দেখছেন এগুলো হলো পাজল। পৃথিবীতে যতো ছোট বড় অ্যাপ্লিকেশন আছে সব তৈরি করা হয়েছে এগুলো দিয়ে। এদের মধ্যে লাস্টেরটা প্রয়োজনভেদে অ্যাডভান্স ডাটা স্ট্রাকচার দিয়ে রিপ্লেস করা যায়। কিন্তু প্রথম ৫টা রিপ্লেসের কোনো সুযোগ নেই। + + সি এর যে চাইল্ড ল্যাঙ্গুয়েজগুলো আছে যেমন সি++, জাভা, জাভাস্ক্রিপ্ট, পিএইচপি এগুলোতে সিনট্যাক্স অনেক কাছাকাছি। ফাউন্ডেশনাল কম্পোনেন্টগুলো ল্যাঙ্গুয়েজ ভেদে শুধু সিনট্যাক্সে ভিন্ন হয়। অন্য কোনো থট প্রসেসে ভিন্ন হয় না। থট প্রসেস একই। শুধুমাত্র সিনট্যাক্স আলাদা। + + - **স্ট্রাকচারালঃ** কোড লেখার চেয়ে কোড বছরের পর বছর ধরে মেইনটেইন করাটা হচ্ছে সবচাইতে কঠিন কাজ। একটা অ্যাপ্লিকেশন যদি সুন্দরভাবে স্ট্রাকচার করা না যায় তাহলে সেটা মেইনটেইন করা দুরূহ ব্যাপার হয়ে যাবে। সেই স্ট্রাকচারাল প্রব্লেম সলভ করার জন্য আমরা এই স্ট্রাকচারাল প্রসেসিং ব্যবহার করি। এতে আছে + + - OOP: এটা ল্যাঙ্গুয়েজ স্পেসিফিক বিষয় না। গত ক্লাসে আমরা এটা নিয়ে বিষদ আলোচনা করেছিলাম। এটা একটা থিওরাম। যদিও সি অবজেক্ট অরিয়েন্টেড প্রোগ্রামিং ল্যাঙ্গুয়েজ না, কিন্তু আমরা সি তে অবজেক্ট অরিয়েন্টেড থিওরি অ্যাপ্লাই করে OOP এর কাজ করতে পারি। তাহলে দেখা যাচ্ছে যে ল্যাঙ্গুয়েজ আমাকে OOP এর সাপোর্ট দিচ্ছে সেখানেও আমরা OOP ব্যবহার করতে পারছি, যেখানে সাপোর্ট দিচ্ছে না সেখানেও আমরা পারছি। + - Functional: এটাও কোনো ল্যাঙ্গুয়েজ স্পেসিফিক না। কারণ এটা একটা থিওরাম। আর এটা ব্যবহার করে আমরা ফাংশনকে ভ্যালু হিসেবে ইউজ করতে পারি, এবং ফাংশন দিয়েই আমরা দুনিয়ার সবকিছু বানিয়ে ফেলতে পারি। এক্ষেত্রেও যে ল্যাঙ্গুয়েজ আমাকে ফাংশনাল এর সাপোর্ট দিচ্ছে সেখানেও আমরা ফাংশনাল ব্যবহার করতে পারছি, যেখানে সাপোর্ট দিচ্ছে না সেখানেও আমরা পারছি। + - Design Pattern: এটা কোনো প্রোগ্রামিং ল্যাঙ্গুয়েজ ডিপেন্ডেন্ট না। যেহেতু OOP প্রোগ্রামিং ল্যাঙ্গুয়েজ ডিপেন্ডেন্ট না, আমরা OOP বিভিন্ন ওয়েতে ব্যবহার করে ডিজাইন প্যাটার্ন বানিয়ে থাকি। তাই এটিও প্রোগ্রামিং ল্যাঙ্গুয়েজ ডিপেন্ডেন্ট না। + - Design Principles: এটাও কোনো প্রোগ্রামিং ল্যাঙ্গুয়েজের উপর ডিপেন্ড করে না। এতে আছে SOLID (Single responsibility, Open-closed, Liskov substitution, Interface Segregation, and Dependency inversion), DRY (Don't Repeat Yourself), KISS (Keep It Simple, Stupid), YAGNI (You Aren’t Gonna Need It), SoC (Separation of Concerns), Avoid Premature Optimization, Law of Demeter, Clean Code Architecture. মোটামুটি এই ৭/৮ ধরণের প্রিন্সিপাল জানলে যেকোনো কিছু করতে পারবো। এই বিষয়গুলোর আইডিয়া পেতে এই [লিংক](https://www.geeksforgeeks.org/7-common-programming-principles-that-every-developer-must-follow/) ভিজিট করতে পারেন। + - DSA: এখানে একটা প্রশ্ন আসে, ডিএসএ কোনো ল্যাঙ্গুয়েজ ব্যবহার করে শিখা বেটার। আমরা আসলে বুঝিই না ডিএসএ কি জিনিস? ডিএসএ হচ্ছে একধরণের ম্যাথমেটিক্স। গণিতের একটা শাখা আছে ডিসক্রিট ম্যাথমেটিক্স। ডাটা স্ট্রাকচার এবং অ্যালগরিদম এই শাখার অন্তর্ভুক্ত। প্রোগ্রামিং ল্যাঙ্গুয়েজ আসার অনেক আগে থেকে ডিএসএ পৃথিবীতে রয়েছে। যেকোনো ল্যাঙ্গুয়েজ ব্যবহার করেই তা প্রয়োগ করা যায়। ডাটা স্ট্রাকচার হলো আমার ডাটাকে কোনো একটা উপায়ে স্ট্রাকচার করে রাখা, যেন আমি সহজে ডাটা ইনসার্ট করতে পারি, ডাটা বের করে আনতে পারি, প্রয়োজনবোধে ডাটা আপডেট এবং ডিলিট করতে পারি। এখন কোন কাজটা করবো সেই অনুযায়ী আমরা ডাটা স্ট্রাকচার সিলেক্ট করবো। তবে একটা কথা পরিষ্কার মনে রাখতে হবে যে ডিএসএর সাথে প্রোগ্রামিং ল্যাঙ্গুয়েজের কোন সম্পর্ক নাই। + +কিন্তু অ্যাপ্লিকেশনের যে মেইন প্রব্লেম সেটা সলভ করা যায় না স্ট্রাকচারাল প্রসেসিং দিয়ে। তার জন্য কিছু টপিক লাগে যেগুলোকে বলা হয় কম্পিউটার ফান্ডামেন্টালস। যেগুলো ফাউন্ডেশনাল প্রসেসিং এ আলোচনা করা হয়েছে। + +উপরের দুইটা কম্পোনেন্টের মধ্যে সবচেয়ে গুরুত্বপূর্ণ হলো ফাউন্ডেশনাল কম্পোনেন্ট। পৃথিবীর যত সমস্যা আছে সব সলভ করা হয়েছে এটা দিয়ে। স্ট্রাকচারাল লাগে ডেভেলপমেন্টের উদ্দেশ্যে। যতক্ষণ ফাউন্ডেশনালে আমরা কমফোর্টেবল না ততক্ষণ আমরা স্ট্রাকচারালে যাওয়া উচিৎ না। ফাউন্ডেশনালে আমরা কাজ করবো ইম্পেরেটিভ ওয়েতে। + +- **আউটপুটঃ** প্রসেসিং এর পর আমাদের কাছে একটা আউটপুট আসবে। আউটপুট হতে পারে মনিটর, জাভাস্ক্রিপ্ট ডম, প্রিন্টার, সাউন্ডবক্স ইত্যাদি। কিন্তু যখন আমরা প্রথম প্রোগ্রামিং ল্যাঙ্গুয়েজ শিখবো তখন আমরা স্ট্যান্ডার্ড আউটপুট নিয়ে কাজ করবো। পরবর্তীতে কাজ করতে করতে বাকিগুলো শিখে নিবো + +## Visualize Programming Syntax + +আমাদের প্রোগ্রাম করতে গেলে ভয় লাগে কেন? কারণ মানুষ যখন চিন্তা করে তখন সে একটা ইমেজ দিয়ে চিন্তা করে। কিন্তু প্রোগ্রামিং এর ক্ষেত্রে আমাদের ইমেজ নিয়ে চিন্তা করতে সমস্যা হয়। আমরা শুধু সিনট্যাক্স নিয়েই ভাবি। সেক্ষেত্রে সমস্ত সিনট্যাক্স আমাদের মাথায় গিয়ে প্যাঁচ খেয়ে যায়। তখন আমরা অনেক সমস্যার মধ্যে পড়ি। যদি আমরা প্রোগ্রামিং সিনট্যাক্সগুলোকে ইমেজের মতো করে কল্পনা করতে পারি তাহলে আমাদের জন্য অনেকটা সহজ হয়ে যায়। এর জন্য একটা ওয়েবসাইট আছে যার নাম [Scratch](https://scratch.mit.edu/)। যারা বিগিনার তারা এখানে গিয়ে পাজলের মতো মিলিয়ে মিলিয়ে সুন্দরভাবে শিখতে পারবেন। এটা নিয়ে তামিম শাহরিয়ার সুবিন ভাইয়ের [Scratch Programming in Bangla](https://youtube.com/playlist?list=PLym69wpbTIIEkUnqkOznZfQU6lRxebpO3) প্লেলিস্টটি দেখতে পারেন। যারা বিগিনার না প্রোগ্রামিং এ তাদের জন্যও স্ক্র্যাচ ভিজ্যুয়ালাইজেশনের জন্য খুব উপকারী হবে। + +## জাভাস্ক্রিপ্টকে Weird Language বলার কারণ + +জাভাস্ক্রিপ্টকে এমনটা বলার কারণ হলো এখানে OOP আর functional এমনভাবে আছে যেটাতে বিগিনাররা কনফিউজ হয়ে যায়। এখানে যেমন OOP এর কাজ করা যায়, তেমনি যখন মেথড লিখবো সেখানে আবার ফাংশনের সমস্ত বিষয় ব্যবহার করতে পারবো। এটা ক্লিয়ার করতে হহবে খুব ভালভাবে। আরেকটা প্রব্লেম আছে যেটা হলো সেটা হলো জাভাস্ক্রিপ্ট ডাইনামিক এবং উইকলি টাইপড ল্যাঙ্গুয়েজ। জাভাস্ক্রিপ্ট এক ডাটা টাইপকে অন্য ডাটা টাইপে কনভার্ট করার চেষ্টা করে যেটা একটা প্রব্লেম। উদাহরণস্বরূপঃ + +```js +let a = 'ab'; +console.log(Number(a)); // NaN +``` + +এটা যদি পাইথনে হতো সে এরর দিতো। কিন্তু জাভাস্ক্রিপ্ট সেটাকে কনভার্ট করার চেষ্টা করে, এবং ফলস্বরূপ আউটপুট দেয় `Nan`। এসব কারণে জাভাস্ক্রিপ্টকে উইয়ার্ড ল্যাঙ্গুয়েজ বলা হয়। + +## ফাংশন আমরা কেন ব্যবহার করবো + +ধরেন আমাদের একটা রিকোয়ারমেন্ট আছে দুইটা সংখ্যা যোগ করার। টোটাল ছয়টা ক্যালকুলেশন হবে এই যোগের। আমরা ইম্পেরেটিভ ওয়েতে লেখা শুরু করলাম। + +```js +let a = 10 + 20; +let b = 20 + 30; +let c = 40 + 50; +let d = 10 + 20; +let e = 20 + 30; +let f = 40 + 50; +``` + +এরপর হঠাৎ ক্লায়েন্ট এসে বললো ভাই এখানে যে যোগ করার কথা বলেছিলাম, সেটা যোগ হবে না সেটা হবে বিয়োগ। এবার আপনি আবার বসে বসে সব চেইঞ্জ করলেন এভাবে। + +```js +let a = 10 - 20; +let b = 20 - 30; +let c = 40 - 50; +let d = 10 - 20; +let e = 20 - 30; +let f = 40 - 50; +``` + +এবার আবার ক্লায়েন্ট এসে বললো ভাই ঐ যে বলেছিলাম বিয়োগ হবে সেটা আমি ভুল করেছি। সেটা বিয়োগ হবে না, যোগই হবে কিন্তু প্রতিটা থেকে এক বিয়োগ হবে। মাথা আপনার ফায়ার। কিন্তু কিছু করার নাই আবার আপনি করলেন এভাবে। + +```js +let a = 10 + 20 - 1; +let b = 20 + 30 - 1; +let c = 40 + 50 - 1; +let d = 10 + 20 - 1; +let e = 20 + 30 - 1; +let f = 40 + 50 - 1; +``` + +এবার আপনি চিন্তা করলেন ক্লায়েন্ট সুবিধার না। সে এসে বিভিন্ন সময় বিভিন্ন চেইঞ্জ দিচ্ছে। আর এখানে ছয়টার জায়গায় ছয় হাজার যদি হয় তখন তো পাগল হয়ে যেতে হবে। আপনি করলেন কি বুদ্ধি করে একটা ফাংশন বানিয়ে নিলেন এভাবে। + +```js +function myFunction(a, b) { + return a + b - 1; +} +``` + +আর ভ্যারিয়েবলগুলোকে লিখলেন এভাবে। + +```js +let a = myFunction(10, 20); +let b = myFunction(20, 30); +let c = myFunction(40, 50); +let d = myFunction(10, 20); +let e = myFunction(20, 30); +let f = myFunction(40, 50); +``` + +এবার যদি ক্লায়েন্ট এসে বলে ভাই ১ বিয়োগ হবে না। তাহলে আপনি শুধু একটা জায়গায় চেইঞ্জ করবেন। সেটা হলো ফাংশন থেকে `- 1` কেটে দিবেন। তাহলে ফাংশন দাঁড়াবে এরকম। + +```js +function myFunction(a, b) { + return a + b; +} +``` + +এরপর যদি সে বলে আমি আসলে চাইছি প্রতিটা সংখ্যার বর্গের যোগফল। কোনো সমস্যা নাই। ফাংশন বডি চেইঞ্জ করুন। + +```js +function myFunction(a, b) { + return a ** a + b ** b; +} +``` + +তাহলে দেখা যাচ্ছে ফাংশনের মাধ্যমে আমাদের কাজ কমে যাচ্ছে, ডিবাগিং এ সুবিধা হচ্ছে, মেইনটেইন করতে সুবিধা হচ্ছে কোড, এবং কোড রিইউজও করতে পারছি। যখন দেখবো কোনো কোড আমাদের দুইবার লিখতে হচ্ছে সেখানেই আমরা ফাংশন ইউজ করবো। যেখানেই কোড রিপিটেশন সেখানেই ফাংশন। + +আমাদের বিগিনারদের দুই জায়গায় মূলত প্রব্লেম হয়। একটা হচ্ছে ভ্যারিয়েবল ডিক্লেয়ার করা। আরেকটা হচ্ছে ফাংশন ডিক্লেয়ার করা। এর সমাধান হচ্ছে- + +- যখনই দেখবো আমার কাছে একটা ডেটা আছে, আমরা সেই ডেটার জন্য একটা ভ্যারিয়েবল বানিয়ে ফেলবো। +- প্রথমে আমাদের যা কাজ আছে আমরা করতে থাকবো। করতে করতে যখন দেখবো যে একই কাজ আমরা একের অধিক করেছি, সাথে সাথে আমরা তার জন্য একটা ফাংশন লিখে ফেলবো। এখানে আমরা আমাদের অজান্তে একটা ডিজাইন প্রিন্সিপাল অ্যাপ্লাই করে ফেলেছি। সেটা হলো DRY। + +## শেষ কথা + +স্ট্রাকচারাল কম্পোনেন্টকে যদি ধরেন বিল্ডিং এর ডিজাইন, রঙ তাহলে ফাউন্ডেশনাল হলো ইট, বালি, সিমেন্ট, রড। আপনার বিল্ডিং যতোই সুন্দর হোক না কেন ইট, বালি, সিমেন্ট, রড যদি ভাল না হয় তাহলে সে বিল্ডিং টিকবে না। তাই আমাদের ফাউন্ডেশনাল ক্লিয়ার করেই স্ট্রাকচারালে যাওয়া উচিৎ। ফাউন্ডেশনালে কোনো ধরণের ল্যাকিংস রাখা উচিৎ না। + + + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/resources/lecture-03/Programming Language Landscape.drawio b/docs/Lectures/Fundamentals/03/Programming Language Landscape.drawio similarity index 100% rename from resources/lecture-03/Programming Language Landscape.drawio rename to docs/Lectures/Fundamentals/03/Programming Language Landscape.drawio diff --git a/class-overview/Lecture-03/Programming Language Landscape.png b/docs/Lectures/Fundamentals/03/Programming Language Landscape.png similarity index 100% rename from class-overview/Lecture-03/Programming Language Landscape.png rename to docs/Lectures/Fundamentals/03/Programming Language Landscape.png diff --git a/resources/lecture-03/app.js b/docs/Lectures/Fundamentals/03/app.js similarity index 100% rename from resources/lecture-03/app.js rename to docs/Lectures/Fundamentals/03/app.js diff --git a/docs/Lectures/Fundamentals/03/resource.md b/docs/Lectures/Fundamentals/03/resource.md new file mode 100644 index 0000000..b57988a --- /dev/null +++ b/docs/Lectures/Fundamentals/03/resource.md @@ -0,0 +1,56 @@ +# Resource +## Lecture 3 - Programming Language Foundation - A Bigger Landscape + +- [Programming Language Landscape](./Programming%20Language%20Landscape.png) +- Visualize Programming Syntax e.g. [Scratch](https://scratch.mit.edu/) + +## When we use function? + +If requirements change frequently, we use function. You can see [this code](./app.js) for a quick understanding. + +### Important Links + +- [GeeksForGeeks](https://www.geeksforgeeks.org/) +- [Scratch](https://scratch.mit.edu/) +- [Scratch Tutorial Playlist](https://youtube.com/playlist?list=PLym69wpbTIIEkUnqkOznZfQU6lRxebpO3) +- [Class Overview](../../class-overview/Lecture-03/README.md +- [Understanding the origin of fundamental concepts] (https://www.tutorialspoint.com/computer_programming/index.htm) + +### Today's Task + +- Do a research to find the origins of the fundamentals. + + + + +
+ App.js File +

Variables helps us to make thing dynamic.

+ +```js +/* let a = 10 + 20 - 1; +let b = 20 + 30 - 1; +let c = 40 + 50 - 1; +let d = 10 + 20 - 1; +let e = 20 + 30 - 1; +let f = 40 + 50 - 1; */ + +let a = myFunction(10, 20); +let b = myFunction(20, 30); +let c = myFunction(40, 50); +let d = myFunction(10, 20); +let e = myFunction(20, 30); +let f = myFunction(40, 50); + +function myFunction(a, b) { + // return a + b - 1; + // return a + b; + return a ** a + b ** b; +} + +/** + * If we have data, make a variable for it + */ +``` + +
diff --git a/docs/Lectures/Fundamentals/04/Overview.md b/docs/Lectures/Fundamentals/04/Overview.md new file mode 100644 index 0000000..5480ad1 --- /dev/null +++ b/docs/Lectures/Fundamentals/04/Overview.md @@ -0,0 +1,624 @@ +## Lecture 04 - Programming Fundamentals using JavaScript + +লাস্ট ক্লাসে আমরা প্রোগ্রামিং ল্যাঙ্গুয়েজের যে ফান্ডামেন্টাল বিষয়গুলো নিয়ে আলোচনা করেছিলাম আজ সেগুলোর অরিজিন সম্পর্কে জানবো। অরিজিন সম্পর্কে জানলে আমাদের আর এসব বিষয় নিয়ে চিন্তা করতে হবে না। প্রোগ্রামিং ল্যাঙ্গুয়েজের যে ফান্ডামেন্টালস আছে তা হলোঃ + +- Variables +- Operators +- Conditions +- Loops +- Arrays +- Objects +- Functions +- Expression vs Statement + +## Variables + +আমরা জানি ভ্যারিয়েবল মানে চলক। বিগিনারদের একটা সমস্যা হয় যে কোন জায়গায় ভ্যারিয়েবল নিতে হবে আর কোন জায়গায় নিতে হবে না। এর জন্য কিছু ট্রিকস আছে। তার আগে আমাদের জানা দরকার ভ্যারিয়েবলের কাজটা কি? সহজ ভাষায় বলতে গেলে ভ্যারিয়েবল আমাদের যেকোনো কিছু ডায়নামিক করতে সাহায্য করে। এটাই একমাত্র কাজ। এটা ছাড়া ভ্যারিয়েবলের আর কোনো কাজ নেই। এটা অনেকটা পাত্রের মতো। পাত্র যতো বেশি লাগবে জায়গা ততো বেশি লাগবে। মানে বেশি মেমোরি লাগবে। তাহলে এক্সট্রা মেমোরির দরকার কি? ভ্যারিয়েবল কম নিলেই তো হয়। তাহলে আমাদের অ্যাপ্লিকেশন অনেক হালকা হবে। কিন্তু তা সত্ত্বেও আমাদের ভ্যারিয়েবল নিতে হয়। যেমন ধরেন আমরা পানি খাবো। এই পানি কিভাবে আমাদের কাছে আসবে? প্রথমে মাটির নিচ থেকে পানি পাইপ এবং পাম্পের মাধ্যমে বাসার নিচে একটা ট্যাংকে জমা হয়। এরপর আরেকটা পাম্পের মাধ্যমে তা ছাদের উপর গিয়ে আরেকটা ট্যাংকে জমা হয়। ওখানে থেকে পাইপিং এর মাধ্যমে বাসায় বাসায় ডিস্ট্রিবিউট করা হয়। বাসায় ট্যাপের মাধ্যমে সেই পানি আমরা ফিল্টারের মধ্যে জমা করি। ফিল্টার হওয়ার পর সেই পানি আমরা জগ বা বোতলে নিয়ে রাখি। এরপর যখন পানি খাওয়ার প্রয়োজন হয় তখন আমরা জগ বা বোতল থেকে গ্লাসে ঢেলে পানি খাই। আমাদের উদ্দেশ্য কিন্তু পানি খাওয়া তাহলে মাঝখানে এত জায়গায় পানি জমা করে রাখার প্রয়োজন কি? যদি ট্যাংকে জমা রাখতে না পারতাম তাহলে সবার ঘরে ঘরে ডিস্ট্রিবিউট করা সম্ভব হতো না। যদি ফিল্টারে না রাখতাম তাহলে পানি বিশুদ্ধ করতে পারতাম না। সুতরাং ভ্যারিয়েবলও এরকম। এরা ডাটা স্টোর করে রাখে। ভ্যারিয়েবল যদি আমরা না নিই তাহলে আমরা ডাটা দ্বিতীয়বার ইউজ করতে পারতাম না। + +```js +console.log('Abu Rayhan', 'Abu Rayhan'.length); // Abu Rayhan 10 +``` + +উপরের উদাহরণে যদি আমরা অন্য কোনো নাম দিয়ে করতে চাই তাহলে সেটা সম্ভব না। সেটা করতে গেলে বর্তমান নামকে বাদ দিতে হবে। কারণ স্ট্যাটিক কোডের ক্ষেত্রে রানটাইমে কোনো কিছু চেইঞ্জ করা যায় না। এখন প্রশ্ন আসতে পারে রানটাইম আর কম্পাইল টাইম কি? + +```js +console.log('Abu Rayhan', 'Abu Rayhan'.length); // Abu Rayhan 10 +throw new Error('Something wrong'); // Error +``` + +এখানে প্রোগ্রামে কোনো ভুল নেই। কিন্তু প্রোগ্রাম রান করতে গিয়ে দেখা যাচ্ছে একটা এরর আছে। একে বলে রানটাইম এরর। প্রোগ্রাম রান করার সময়কে বলা হচ্ছে রানটাইম। + +```js +console.log('Abu Rayhan', 'Abu Rayhan'.length); +121354644dsfsdf +``` + +এক্ষেত্রে প্রথম কোড এক্সিকিউট হবে না। সরাসরি এরর দেখাবে। মানে কম্পাইল করার সময় সে কোডে ভুল পেয়েছে। একে বলে কম্পাইল টাইম এরর। + +এখন রানটাইমে আমরা ডায়নামিক্যালি অনেক কিছু চেইঞ্জ করতে পারি। যেমন কিছু ইনপুট দিতে পারি, ইন্টারনেট থেকে কিছু নিয়ে আসতে পারি, মাউস দিয়ে ক্লিক করতে পারি। এসব কিছুই রানটাইমে হয়। কিন্তু যখন আমরা প্রথম উদাহরণের মতো স্ট্যাটিক ডাটা নিয়ে কাজ করবো তখন আমরা কোনোভাবেই লগের ভিতরে থাকা ডাটা চেইঞ্জ করতে পারবো না। এজন্য আমাদের ভ্যারিয়েবল নিতে হবে। যখনই আমরা দেখবো ডাটা তখনই আমরা একটা ভ্যারিয়েবল নিয়ে নিবো। + +```js +let name = 'Abu Rayhan'; +console.log(name, name.length); // Abu Rayhan 10 +``` + +ভ্যারিয়েবলে নেয়ার কিছু উপকার আছে। প্রথম উপকার হলো এই ডাটাটা আমরা আবু রায়হানের জন্য নিয়েছি। এখন আমি চাইছি জাহিদ হাসানের জন্য নিবো, বা এইচ এম নাঈমের জন্য নিবো। সেক্ষেত্রে শুধু `name` এর মধ্যে থাকা ডাটা চেইঞ্জ করে দিলেই হচ্ছে। আরো একটা উপকার হলো এখানে `name` শুধু দুই জায়গাতে ব্যবহার হয়েছে। এটা দুই জায়গায় না হয় দুইহাজার হতে পারতো, দুই লক্ষ হতে পারতো। এখন কোনো কারণে ডাটা চেইঞ্জ করতে হলে আমার পক্ষে দুই লক্ষ জায়গায় চেইঞ্জ করা কি সম্ভব? সেক্ষেত্রে ভ্যারিয়েবল নিয়ে নিলে আমরা শুধু এক জায়গাতেই চেইঞ্জ করবো। বাকি দুই লক্ষ জায়গায় আর দৌঁড়াতে হলো না। + +আরেকটা উদাহরণ দিলে ডায়নামিক্যালি কিভাবে রানটাইমে ডাটা পরিবর্তন করা যায় ভ্যারিয়েবলের মাধ্যমে তা স্পষ্ট হয়ে যাবে। + +```js +const names = [ + 'HM Nayeem', + 'Aditya Chakraborty', + 'Abu Rayhan', + 'Shaker Hossain', + 'Akib Ahmad', + 'Alvi Chowdhury', +]; +let index = -1; +let person = names[++index]; + +setInterval(() => { + person = names[index++]; + console.log(person, person.length); + + if (index === names.length) { + index = 0; + } +}, 1000); +``` + +এখন প্রোগ্রামটি রান করলে দেখা যাবে ১ সেকেন্ড পরপর নামগুলো পরিবর্তন হয়ে একটার পর একটা আসতে থাকবে। শেষ হয়ে গেলে আবার প্রথম থেকে শুরু করবে। এর কোনো শেষ থাকবে। এখন যদি আমরা ভ্যারিয়েবল ব্যবহার না করে জাস্ট একটা নাম বসিয়ে দিই, তাহলে শুধুমাত্র সেই নামটাই প্রিন্ট হবে বারবার। ডায়ামিক ব্যাপারটা আর থাকলো না। আর আমরা প্রোগ্রামিং করিই সবকিছু ডায়নামিক করার জন্য। তাই যেখানেই দেখবো ডাটা সেখানেই আমরা ভ্যারিয়েবল ধরে নিবো। এক্সট্রা মেমোরি যায় যাক। + +এবার আসি দুইটা গুরুত্বপূর্ণ টার্ম `let` এবং `const` নিয়ে। কখন `let` আর কখন `const` ব্যবহার করবো। যখন দেখবো কোনো ডাটা আমার আর পরবর্তীতে আপডেট হচ্ছে না সেক্ষেত্রে আমরা `const` ব্যবহার করবো। তাহলে আমরা চাইলেও বা ভুল করেও সেই ডাটা আপডেট করতে পারবো না। করতে গেলে এরর চলে আসবে। আর যেসব ডাটা পরবর্তীতে আপডেট হওয়ার চান্স থাকে সেসব ডাটার ক্ষেত্রে আমরা `let` ব্যবহার করবো। উপরের উদাহরণে `names` কোথাও আপডেট হচ্ছে না। তাই আমরা `const` ব্যবহার করেছি। কিন্ত `index` এবং `person` আমরা আপডেট করেছি পরবর্তীতে। তাই আমরা সেখানে `let` ব্যবহার করেছি। নিয়ম হলো সবকিছু প্রথমে আমরা `const` ধরে নিবো। এরপর যদি কোনো ডাটা আপডেট হচ্ছে দেখা যায় সেটাকে আমরা পরে `let` করে দিবো। এভাবে করলে আমাদের ভুল করার চান্স অনেক কম থাকবে। + +**তাহলে দেখা যাচ্ছে ভ্যারিয়েবল ব্যবহার করা হয় রানটাইমে ডাটা ডায়নামিক্যালি আপডেট করার জন্য।** + +## Operators + +অপারেটরের সাথে আমরা সবাই পরিচিত। ছোটবেলা থেকে আমরা যোগ, বিয়োগ, গুণ, ভাগ করি। ওখানে আমরা কিছু ম্যাথমেটিকাল অপারেটর ব্যবহার করতাম। প্রোগ্রামিং এর অপারেটরসও একই। প্রোগ্রামিং ল্যাঙ্গুয়েজে অপারেটরস ব্যবহার করা হয় গাণিতিক হিসাবনিকাশের জন্য। আর কোনো কাজ নেই এর। অপারেটরস হলো মূলত ফাংশনস। যেসব ল্যাঙ্গুয়েজে অপারেটর ওভারলোডিং আছে সেখানে আমরা ফাংশন তৈরি করে অপারেটরের কাজকর্মগুলো করি। যেমন + +```js +add(); + +mod(); + +lessThan(); +``` + +আমরা যোগ করার জন্য `add` ফাংশন বানিয়ে ব্যবহার করতে পারি। `mod` ফাংশন ইউজ করে আমরা মডুলাস অপারেশন করতে পারি, `lessThan` ব্যবহার করে আমরা আমাদের ভ্যালু ছোট কিনা তা চেক করতে পারি। কিন্তু জাভাস্ক্রিপ্টে অপারেটর ওভারলোড হয় না। তাই আমাদের ফাংশন বানানোর দরকার পড়ে না। জাভাস্ক্রিপ্ট আগে থেকেই কিছু সিম্বলিক রিপ্রেজেন্টশনের মাধ্যমে এসব ফাংশন তৈরি করে রেখেছে, যাতে আমাদের কাজ অনেক সহজ হয়। `add()` এর পরিবর্তে আমরা `+` ব্যবহার করি, `mod()` এর পরিবর্তে `%`, `lessThan` এর পরিবর্তে আমরা `<` ব্যবহার করি। অপারেটরস নিয়ে আসলে আর ডিটেইলস তেমন কিছু বলার নেই। + +## Conditions + +কন্ডিশনকে বলা হয় কম্পিউটারের ব্রেইন। এই কন্ডিশনের উপর ভিত্তি করেই কম্পিউটার সিদ্ধান্ত নেয় কোন কাজটা সে করবে। যদিও কম্পিউটারের ডিসিশন নেয়ার কোনো ক্ষমতা নেই। আমরাই বিভিন্ন লজিক লিখে কম্পিউটারকে একটা সিদ্ধান্ত নেয়ার রাস্তা করে দিই। এখন লজিক লিখতে গেলে আমাদের কন্ডিশন প্রয়োজন। আমরা প্রতিনিয়ত আমাদের প্রাত্যহিক জীবনে কন্ডিশন ব্যবহার করে যাচ্ছি। যেমন যদি আকাশ মেঘলা থাকে তাহলে আমি ছাতা নিয়ে বের হবো, নাহয় ছাতা নিয়ে বের হবো না। এটা একটা কন্ডিশন। আবার ধরুন আমার অফিস ৯টায়। এখন যদি আমি ৮টার মধ্যে ঘর থেকে বের হতে পারি তাহলে বাসে যাবো, নাহয় সিএনজি নিয়ে যাবো। এটাও এক ধরণের কন্ডিশন। আবার যেমন আজকের ক্লাসে যেহেতু বেসিক পড়ানো হবে সেহেতু আমি আজকের ক্লাসে জয়েন করবো না, যদি অ্যাডভান্স পড়ানো হয় তাহলে জয়েন করবো। এখন এই কন্ডিশনকে কিভাবে আমরা কোডে রূপান্তর করতে পারি সেটা দেখা যাক। + +```js +if (studyBasic) { + wontJoin(); +} + +if (studyAdvanced) { + join(); +} + +if (teacherSpeaks) { + silent(); +} + +if (!teacherSpeaks) { + shout(); +} +``` + +এটা একটু আমরা বুঝার চেষ্টা করি। যদি বেসিক পড়ানো হয় তাহলে আজকের ক্লাসে আমি জয়েন করবো না। যদি অ্যাডভান্স পড়ানো হয় তাহলে জয়েন করবো। যদি টিচার কথা বলেন আমরা সবাই চুপ থাকবো। যদি তিনি কথা না বলেন আমরা সবাই একসাথে কথা বলবো। এরকম আমরা আমাদের প্রাত্যহিক কর্মকান্ডের মধ্যে কন্ডিশন খুঁজে পাবো। কন্ডিশন ছাড়া সব অচল। প্রোগ্রামিং ল্যাঙ্গুয়েজের ৫০% হলো কন্ডিশন, আর ৫০% হলো লুপ। + +এখন কন্ডিশন ৩ ভাবে লেখা যায়। অর্থাৎ এর ৩টা সিনারিও আছে। আমরা একটু আগে নিচের কোড দেখি। এরপর ব্যাখ্যা করবো। + +```js +// Scenario 1 - Single branch +// if condition +if (hasMoney) { + buyPhone(); +} + +// Scenario 2 - Two branches +// if else condition +if (toss === 'head') { + win(); +} else { + loss(); +} + +// Scenario 3 - Multiple branches +// else if +let a = 1, + b = 2; +if (a > b) { + big(); +} else if (a < b) { + small(); +} else { + same(); +} +``` + +- Scenario - 1: এটা সিঙ্গেল ব্রাঞ্চ। এখানে একটার বেশি কেইস হওয়া সম্ভব না। সেক্ষেত্রে আমরা এই সিনারিও ব্যবহার করবো। একে বলা হয় `if condition`। এখানে বলা হয়েছে টাকা থাকলে ফোন কিনবো। না থাকলে যেমন যাচ্ছে যাবে। একটার বেশি কেইস এখানে দরকার নেই। তাই এখানে আমরা শুধু `if condition` ব্যবহার করবো। + +- Scenario - 2: এটা মূলত দুইটা ব্রাঞ্চের উপর নির্ভর করে, অর্থাৎ এর সর্বোচ্চ দুইটা আউটকাম থাকবে। একে বলা হয় `if else condition`। এ ধরণের সিনারিওতে দুইটার বেশি কন্ডিশন কখনই সম্ভব হবে না। যেমন যদি টসে হেড আসে তাহলে আমি টস জিতবো, নাহয় হারবো। এই দুইটার বেশি রেজাল্ট আসার সম্ভাবনা নেই। + +- Scenario - 3: এটা মাল্টিপল ব্রাঞ্চ। যদি দুইয়ের অধিক আউটকাম আসার সম্ভাবনা থাকে তাহলে আমরা এটা ব্যবহার করবো। এই ধরণের সিনারিওকে বলে `else if condition`। যেমন যদি `a > b` হয় তাহলে a, b এর চেয়ে বড় হবে। যদি `a < b` হয় তাহলে a, b এর চেয়ে ছোট হবে। নাহয় দুইটাই সমান হবে। এখানে দুইয়ের অধিক আউটকাম এসেছে। তাই এখানে আমরা মাল্টিপল ব্রাঞ্চ ব্যবহার করেছি। + +কন্ডিশন নির্ভর করে মূলত আউটকামের উপর। আমার কয়টা আউটকাম আসতে পারে, সেভাবে কন্ডিশন লিখতে হবে। + +## Loop + +লুপের একটাই কাজ। একই কাজ বারবার করা। যেমন আমাদের একজন ক্লায়েন্ট এসে বললেন বাংলাদেশ তো এবার ৫১ বছরে পা দিলো। আমরা আমাদের অ্যাপ্লিকেশনে এমন কিছু করবো যেন ৫১ বার 'We love Bangladesh' লেখা আসে। আমরা অনেক বুদ্ধিমান আমরা ১ ঘন্টা ধরে গুণে গুণে ৫১ বার `console.log('We love Bangladesh')` লিখে ক্লায়েন্টকে দেখালাম। ক্লায়েন্ট বললো না ভাই ৫১ বার না। যেহেতু ১৯৭১ সালে বাংলাদেশ স্বাধীন হয়েছিল আমরা ১৯৭১ বার চাইছি। এবার আমি ৩ দিন ধরে গুণে গুণে সেই একই কোড লিখে বিধ্বস্ত অবস্থায় ক্লায়েন্টের কাছে নিয়ে দেখালাম। ক্লায়েন্ট বললো, নাহ্‌। এভাবে না। আমরা ৩০ লাখ শহীদকে সম্মান দেখিয়ে ৩০ লক্ষ বার এটা প্রিন্ট করবো এবার আমি বেহুঁশ। আমি গিয়ে রিসার্চ করে দেখলাম জাভাস্ক্রিপ্টে `repeat()` নামক একটি ফাংশন আছে। এবার তো ইজি। ক্লায়েন্ট যতবার চায় ততবার লিখে দিতে পারবো জাস্ট এক লাইন কোড লিখে। আমিও নিচের কোড লিখে ক্লায়েন্টের কাছে খুব গর্ব নিয়ে গেলাম জাস্ট ১০ মিনিটের মধ্যে। + +```js +console.log('We love Bangladesh\n'.repeat(3000000)); +``` + +আমি তো আকাশে উড়ছি। মনে মনে জাভাস্ক্রিপ্টকে ধন্যবাদ দিচ্ছি এই ফাংশনের জন্য। এবার ক্লায়েন্ট দেখে খুব খুশি হলেন। কিন্তু কিছুক্ষণ পরে বললেন, আচ্ছা ৩০ লক্ষ বার তো প্রিন্ট হলো। ইউজার কিভাবে বুঝবে এটা ৩০ লক্ষ বার হয়েছে। প্রতিটার আগে একটা নাম্বার দিয়ে দিলে বিষয়টা আরো ভাল হয়। এবার তো মাথায় হাত। ডায়নামিক্যালি এত নাম্বার কিভাবে করবো? তার জন্য সহজ সমাধান নিয়ে এসেছে প্রোগ্রামিং ল্যাঙ্গুয়েজ। সেটা হলো লুপ। লুপের মাধ্যমে আমরা সহজেই একই কাজ বারবার করাতে পারি যতবার খুশি। + +```js +for (let i = 1; i <= 3000000; i++) { + console.log(i, 'We love Bangladesh'); +} +``` + +এবার সব সমস্যার সমাধান হয়ে গেলো। লুপ আমাদের রিপিটেশনের কাজকে অনেক সহজ করে দিয়েছে। ধরে নেন লুপের মধ্যে কার্লি ব্যাকেটের ব্লকটা একটা নতুন js ফাইল। আমরা এখানে যেকোনো ভ্যালিড js কোড লিখতে পারি। কিন্তু মনে রাখতে হবে এই ব্লকের মধ্যে যা কোড থাকবে তা মাল্টিপল টাইম এক্সিকিউট হবে। + +প্রধাণত ৩ ধরণের লুপ আছে জাভাস্ক্রিপ্টে। এরপর বিভিন্ন কাজের সুবিধার্থে লুপের সংখ্যা বৃদ্ধি পেয়েছে। মাল্টিপল আইপের লুপ থাকলেও যেকোনো একটা লুপ ব্যবহার করেই প্রব্লেম সলভ করার যাবে। + +- for loop + - range + - for in + - for of +- while loop +- do while loop + +### For Loop + +For লুপ দিয়ে আমরা while, do while দুই ধরণের কাজই করা যায়। এখন for loop কেন আসলো, এর কাজটা কি? আমরা আমাদের আগের উদাহরণটাই নিই। এখানে ক্লায়েন্ট বলে দিয়েছেন ৩০ লক্ষ বার প্রিন্ট করার জন্য। তার মানে আমার রেঞ্জ জানা আছে। আমার স্টার্টিং পয়েন্টও জানা আছে, আবার কোথায় গিয়ে থামতে হবে তাও জানা আছে। এক্ষেত্রে আমরা সবসময় ফর লুপ ব্যবহার করবো। তার মানে হলো যেসব ক্ষেত্রে আমাদের স্টার্টিং পয়েন্ট এবং এন্ডিং পয়েন্ট জানা থাকবে সেসব ক্ষেত্রে আমরা ফর লুপ ব্যবহার করবো। ফর লুপের মধ্যে আবার ৩ ধরণের লুপ রয়েছে। একটা হলো রেঞ্জ রিলেটেড যেটা দেখলাম। আরেকটা for in যেটা আমরা অ্যারে বা অবজেক্টের ক্ষেত্রে ব্যবহার করতে পারি। যেটাতে রেঞ্জ দিতে হয় না in ব্যবহার করে আমরা কাজ করতে পারি। সেটা আমরা পরে দেখবো। আরেকটা হল for of loop। এটা ইটারেটর আসার পর সহজে কাজ করার জন্য এসেছে। এটাও অ্যারে নিয়ে কাজ করতে ব্যবহৃত হয় বা যেকোনো ইটারেটরের ক্ষেত্রে এটা ব্যবহৃত হয়, সেটাও আমরা পরে আলোচনা করবো। এটা খুব কাজে লাগবে যখন আমরা asynchronous data নিয়ে কাজ করবো তখন। + +### While Loop + +While loop দিয়েও আমরা for, do while এর কাজ করতে পারি। এখন এটা কেন আসলো, এর কাজ কি? ধরেন ক্লায়েন্ট আমাদের বললো আমরা লুপটা র‍্যান্ডমলি চালাবো। ১ থেকে চালাতে হবে সেরকম কথা নেই। জাস্ট একটা র‍্যান্ডম নাম্বার থাকবে আর পাশে 'We love Bangladesh' কথাটা থাকবে। কতক্ষণ চালাতে হবে তার কোনো বর্ণনা নেই। আবার ইনফিনিট লুপও চালানো যাবে না। ক্লায়েন্ট বললো তুমি একটা কাজ করো। যখনই নাম্বার চলতে চলতে ৭১ এ আসবে তখনই আমার প্রিন্টিং বন্ধ হয়ে যাবে। নিচের উদাহরণ দেখলে আরো স্পষ্ট হবে আপনাদের কাছেঃ + +```js +while (true) { + let num = Math.ceil(Math.random() * 100); + console.log(num, 'We love Bangladesh'); + if (num === 71) break; +} +``` + +ফর লুপের সাথে এর পার্থক্য হলো ফর লুপের ৩টা পার্ট ছিল। ইনিশিয়ালাইজ, কন্ডিশন, ইনক্রিমেন্ট/ডিক্রিমেন্ট। কিন্তু হোয়াইল লুপে আছে শুধু একটা পার্ট, সেটা হলো কন্ডিশন। আর কন্ডিশন মানেই হলো হয় সত্য হবে নাহয় মিথ্যা। তার মানে যখন আমার কোনো রেঞ্জ জানা থাকবে না, শুধু কন্ডিশন জানা থাকবে তখনই আমরা হোয়াইল লুপ ব্যবহার করবো। + +### Do While Loop + +Do While দিয়ে আমরা কিন্তু for বা do while এর কাজ করতে পারি না। এটা একটা স্পেশাল টাইপের লুপ। এটা কেন ব্যবহার করা হয়। ধরেন হোয়াইল লুপের উদাহরণে কন্ডিশন সত্য না হয়ে মিথ্যা হলো। তাহলে সে একবারও আউটপুট শো করবে না। আমি চাইছি সত্য হোক বা মিথ্যা হোক আউটপুট অন্তত একবার হলেও শো করবে। সেক্ষেত্রে আমরা do while loop ব্যবহার করবো। এটা হোয়াইল লুপ দিয়েও সম্ভব। কিন্তু ডু হোয়াইল দিয়ে আরেকটু বেটার কাজ করা যায়। একটি উদাহরণ দেখি আমরা। + +```js +do { + console.log('It will run at least once'); +} while (false); +``` + +যদিও এর কন্ডিশন মিথ্যা কিন্তু তাও অন্তত একবার এটি আউটপুট দেখাবে। এটাই হলো এই লুপের কনসেপ্ট। + +## Array + +সবচেয়ে অবহেলিত ডাটা স্ট্রাকচার এবং সবচেয়ে শক্তিশালী ডাটা স্ট্রাকচারের মধ্যে অন্যতম। অ্যারে ব্যবহার করে আমরা অনেক কমপ্লেক্স ডাটা স্ট্রাকচার তৈরি করে ফেলতে পারি। যেমন গ্রাফ, হীপ, স্ট্যাক, কিউ। ২/৩ লক্ষ ডাটা নিয়ে কাজ করার জন্য অ্যারে অনেক পাওয়ারফুল এবং পারফেক্ট ডাটা স্ট্রাকচার। প্রশ্ন হলো অ্যারে কেন আসছে আর অ্যারের কাজ কি? আমরা কয়েকজন মানুষের নাম স্টোর করে রাখতে চাইছি। সেটা আমরা ভ্যারিয়েবল দিয়ে করতে পারি। + +```js +const name1 = 'Rayhan'; +const name2 = 'Alvi'; +const name3 = 'Anik'; +const name4 = 'Arjun'; +const name5 = 'Ayman'; +``` + +এখন আমি চাইছি এই নামগুলোকে লোয়ারকেসে কনভার্ট করবো। সেটার জন্য আমাদের প্রত্যেকটা ধরে ধরে কনভার্ট করতে হবে। + +```js +console.log(name1, name1.toLowerCase()); +console.log(name2, name2.toLowerCase()); +console.log(name3, name3.toLowerCase()); +console.log(name4, name4.toLowerCase()); +console.log(name5, name5.toLowerCase()); +``` + +এখন এখানে তো ৫টা নাম না থেকে তো ৫ লক্ষ নাম থাকতে পারতো। সেগুলোকে যদি প্রত্যেকটা ধরে ধরে কনভার্ট করতে হয় তাহলে সেটা তো একটা অসম্ভব ব্যাপার। তার জন্য আমাদের এমন একটা উপায় দরকার যাতে করে আমরা সব নামকে একটা ভ্যারিয়েবলের মধ্যে রাখতে পারি। এখন একটা ভ্যারিয়েবলের মধ্যে রেখে দেখি। + +```js +const persons = 'Rayhan, Alvi, Anik, Arjun, Ayman'; +``` + +এখন এখানে প্রব্লেম হলো এগুলো সব মিলে একটা বড় স্ট্রিং এ পরিণত হয়ে গেছে। এখানে থেকে আলাদা করবো কিভাবে? আলাদা আলাদা ভাবে স্টোর করে দেখা যাক। + +```js +const persons = 'Rayhan', 'Alvi', 'Anik', 'Arjun', 'Ayman'; +``` + +কিন্তু এখন প্রোগ্রাম রান করাতে গেলে অনেক বড়সড় একটা এরর খেয়ে বসে থাকবো আমরা। এই সমস্যা সমাধানের জন্য আমাদের কাছে একটা ডাটা স্ট্রাকচার আছে যার নামে অ্যারে। কিছু না জাস্ট উপরের উদাহরণের আগে আর পরে `[]` বসিয়ে দিলেই অ্যারে হয়ে যাবে। + +```js +const persons = ['Rayhan', 'Alvi', 'Anik', 'Arjun', 'Ayman']; +``` + +এবার এখান থেকে ডাটা কিভাবে বের করা যায়? প্রতিটা অ্যারে এলিমেন্টের পজিশনের একটা নাম্বার আছে। একে বলে ইনডেক্স। ইনডেক্স শুরু হয় ০ থেকে। তাহলে ১ম পজিশনের ইনডেক্স ০, ২য় পজিশনের ইনডেক্স ১, ৩য় পজিশনের ইনডেক্স ২ এভাবে করে ইনডেক্সিং করা যায়। তাহলে আমরা অ্যারের একটা নাম পেলাম আর নাম্বার পেলাম। নাম্বার পাওয়ার কারণে সুবিধা হলো আমরা এখানে সহজেই ক্যালকুলেশন করতে পারবো। আর এটাই অ্যারের পাওয়ার। এখন কিভাবে অ্যারে থেকে ডাটা বের করা যায় সেটা দেখি। + +```js +const persons = ['Rayhan', 'Alvi', 'Anik', 'Arjun', 'Ayman']; +console.log(persons[0]); +console.log(persons[1]); +console.log(persons[2]); +console.log(persons[3]); +console.log(persons[4]); +``` + +এভাবে আমরা সব নাম বের করে আনতে পারি। এখন এখানে দেখা যাচ্ছে সব একই শুধু ইনডেক্সটা ভিন্ন। মানে একই কাজ বারবার চলছে। তাহলে এখানে আমরা একটা লুপ চালাতে পারি। + +```js +const persons = ['Rayhan', 'Alvi', 'Anik', 'Arjun', 'Ayman']; + +for (let i = 0; i < 5; i++) { + console.log(persons[i]); +} +``` + +এবার যদি প্রোগ্রাম রান করা হয় তাহলে দেখা যাবে সব নাম সুন্দর করে প্রিন্ট হয়ে যাবে। এখানে একটা প্রব্লেম আছে। প্রব্লেমটা বুঝার জন্য আমরা আরো দুইটা নাম অ্যাড করি। + +```js +const persons = ['Rayhan', 'Alvi', 'Anik', 'Arjun', 'Ayman', 'Ayuub', 'Bidyut']; + +for (let i = 0; i < 5; i++) { + console.log(persons[i]); +} +``` + +এখন যদি প্রোগ্রাম চালাই দেখা যাবে `Ayman` পর্যন্ত এসেই লুপ থেমে যাবে। কারণ আমার লুপের রেঞ্জে দেয়া আছে ইনডেক্স ৫ এর কম হতে হবে। এই সমস্যা সমাধানের জন্য আমরা ৫ এর জায়গায় ডায়নামিক্যালি `persons.length` বসিয়ে দিলেই হয়ে যাবে। তাহলে অ্যারের লেংথ যতোই বাড়ুক সে ডায়নামিক্যালি আপডেট হয়ে যাবে। এবার প্রথম উদাহরণটা যদি আমরা অ্যারে এবং লুপ দিয়ে করি তাহলে কেমন দেখাবে? + +```js +const persons = ['Rayhan', 'Alvi', 'Anik', 'Arjun', 'Ayman', 'Ayuub', 'Bidyut']; + +for (let i = 0; i < 5; i++) { + console.log(students[i], students[i].toLowerCase()); +} +``` + +তাহলে বুঝতেই পারছেন অ্যারে কতটা শক্তিশালী। অ্যারের সাথে ওতপ্রোতভাবে জড়িয়ে আছে লুপ। এই অ্যারে এবং লুপ দিয়ে আমরা অনেক কাজ সহজে এবং কম সময়ে করে ফেলতে পারি। + +অ্যারেতে আমরা কি কি ধরণের ডাটা স্টোর করতে পারি। প্রায় সব প্রোগ্রামিং ল্যাঙ্গুয়েজে অ্যারেতে ডাটা স্টোর করার কিছু লিমিটেশন আছে। আর একটা অ্যারেতে এক ডাটা টাইপেরই ডাটা স্টোর করা যায়। এদিক থেকে জাভাস্ক্রিপ্ট পূর্ণ স্বাধীনতা দিয়েছে। জাভাস্ক্রিপ্টে যেকোনো ডাটা টাইপের ডাটা স্টোর করা যায়। এমনকি একটা অ্যারেতে ভিন্ন ভিন্ন ডাটা টাইপের ডাটাও স্টোর করে রাখা যায়। আমরা একটু নিচের দিকে তাকালে বুঝতে পারবো। + +```js +const nums = [1, 2, 3, 4, 5, 6]; +const bools = [true, true, false, false]; +const nulls = [null, null, null]; +const undefineds = [undefined, undefined, undefined]; +const arrayOfArray = [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], +]; +const mixed = [true, null, 'Str', 5, [12, 2, 4]]; +``` + +এছাড়াও অ্যারেতে অবজেক্ট এবং ফাংশনও স্টোর করা যায়। যেহেতু আমরা এখনও অবজেক্ট ও ফাংশন নিয়ে আলোচনা করিনি তাই আমরা এখানে সেটা দেখালাম না। একটা অ্যারেতে ভিন্ন ডাটা টাইপের ডাটা স্টোর করা গেলেও আমরা একটা অ্যারেতে শুধু একই ডাটা টাইপের ডাটা স্টোর করবো। কারণ হলো ধরেন আপনি ছাত্রছাত্রীর নামের একটা অ্যারে বানালেন। এখন সেখানে যদি আপনি তাদের ঠিকানা, ফোন নাম্বার সব দিয়ে রাখেন তাহলে নাম খুঁজে বের করতে কষ্ট হয়ে যাবে। তাই একটা অ্যারেতে একই টাইপের ডাটা স্টোর করে রাখা উচিৎ। + +অ্যারের কিছু ফাংশনালিটিজ আছে। অ্যারেকে আমরা একটা ডাটাবেজ হিসেবে কল্পনা করতে পারি। ইন মেমোরি ডাটাবেজ। যেখানে আমরা ডাটা ক্রিয়েট করতে পারবো, রীড করতে পারবো, প্রয়োজনে আপডেট করতে পারবো এবং চাইলে ডাটা ডিলিটও করতে পারবো। এই পুরো অপারেশনকে বলা হয় **CRUD - Create, Read, Update, Delete** অপারেশন। দুনিয়াতে যত যত ডাটা স্ট্রাকচার আছে সবকিছুর কাজ এই ঘুরেফিরে CRUD। অ্যারে নিয়ে আরো কিছু আলোচনা আছে। তবে অ্যারে নিয়ে আলোচনা করতে গেলে আমাদের অবজেক্ট সম্পর্কে একটু আলোচনা আগে করা দরকার। + +## Object + +অ্যারেতে আমরা কিছু ছাত্রের নাম লিখলাম। কিন্তু এখন যদি আমরা তাদের ইমেইল, বয়স এবং সে কি বর্তমান ক্লাসে উপস্থিত আছে কিনা সে ইনফরমেশন স্টোর করতে চাই তাহলে অ্যারে দিয়ে করলে একটা প্রব্লেম আছে। কি প্রব্লেম সেটা আমরা একটু দেখার চেষ্টা করি। + +```js +const student = ['Abu', 'Rayhan', 'rayhan@example.com', 25, true]; +sendEMail(students[0]); + +function sendEmail(email) { + console.log('Sending Email to ', email); +} +``` + +এখন আমরা দেখি এখানে কি কি প্রব্লেম হতে পারে। প্রথমে এটা দেখে বুঝার কোনো উপায় নেই কোনটা কি ধরণের ইনফরমেশন। মানে রিডেবিলিটি নেই কোডটার। আমরা যখন কাউকে ইমেইল করতে যাবো তখন কত নাম্বার ইনডেক্সে সেই ইনফরমেশনটা আছে সেটা আমাদের মনে রাখতে হবে। এখন এখানে ৫টা ডাটা বলে নাহয় কোনোরকমে মনে রাখা গেলো। যদি ৫০০০ হয় তখন মনে রাখাটা অনেক দুষ্কর হয়ে যাবে। এই সমস্যার উত্তোরণের জন্য আমরা এটাকে অন্যভাবে লেখার চেষ্টা করি। + +```js +const student = { + firstName: 'Abu', + secondName: 'Rayhan', + email: 'rayhan@example.com', + age: 25, + attend: true, +}; + +sendEMail(students.email); + +function sendEmail(email) { + console.log('Sending Email to ', email); +} +``` + +কিছু লেখা বেশি লিখতে হলেও কোডটা সহজে পড়েই বুঝা যাচ্ছে কোনটা কি ইনফরমেশন। এখন আর আমাকে ইনডেক্স মনে রাখার দরকার নেই। জাস্ট ভ্যারিয়েবলের নামের শেষে একটা ডট (.) বসিয়ে নামটা দিলেই হয়ে যাবে। এটা আগেরটার চেয়ে অনেক বেশি রিডেবল, অনেক বেশি ইনফরমেটিভ। + +এখন আমরা চাইলে অনেকগুলো ছাত্রের ইনফরমেশন একটা অ্যারেতে স্টোর করে রাখতে পারি। কিভাবে পারি? চলুন দেখি + +```js +const student1 = { + firstName: 'Abu', + secondName: 'Rayhan', + email: 'rayhan@example.com', + age: 25, + attend: true, +}; + +const student2 = { + firstName: 'Alvi', + secondName: 'Chowdhury', + email: 'alvi@example.com', + age: 25, + attend: true, +}; + +const student3 = { + firstName: 'Akib', + secondName: 'Ahmad', + email: 'akib@example.com', + age: 25, + attend: true, +}; + +const allStudents = [student1, student2, student3]; + +for (let i = 0; i < allStudents.length; i++) { + sendEmail(allStudents[i].email); +} + +function sendEmail(email) { + console.log('Sending email to', email); +} +``` + +আমরা প্রতিটা ছাত্রের জন্য আলাদা আলাদা অবজেক্ট তৈরি করবো। এরপর প্রতিটা অবজেক্টকে আমরা একটা অ্যারের মধ্যে স্টোর করে রাকবো। অ্যারেতে যে অবজেক্টও স্টোর করে রাখা যায় এই উদাহরণের মাধ্যমে তারও প্রমাণ আপনারা পেয়ে গেলেন। এখন আমি চাইছি সব ছাত্রকে একসাথে একই ইমেইল পাঠাবো। সেক্ষেত্রে আমাদের অ্যারের উপর লুপ চালিয়ে আমরা সহজেই উপরের কোড অনুসারে একসাথে সবাইকে ইমেইল পাঠিয়ে দিতে পারি। লুপ চালিয়ে প্রথমে আমাদের অ্যারের ইনডেক্স নাম্বার দিয়ে ঐ ডাটাকে ধরতে হবে। এরপর অবজক্টের নিয়ম অনুসারে (.) বসিয়ে এরপর প্রোপার্টি নাম দিয়ে দিলেই কাজ শেষ। অ্যারে এবং অবজেক্টের সমন্বয়ে আমরা প্রোগ্রামকে কিভাবে ডায়নামিক করতে পারি তার ছোট একটি উদাহরণ আপনারা দেখলেন। আপনি যতো বড় অ্যাপ্লিকেশনই বানান ঘুরেফিরে এই কাজটাই করবেন। + +## Functions + +ফাংশন আমরা বানাই অনেকটা লুপের মতো কাজ করার জন্য। লুপ আমরা ব্যবহার করি একই কাজ বারবার করার জন্য। ফাংশনও আমরা ব্যবহার করবো একই কাজ বারবার করার জন্য। তাহলে লুপ থাকতে কেন আমরা ফাংশন ব্যবহার করবো? + +ফাংশন আমরা বিভিন্ন জায়গায় আমাদের মতো করে ব্যবহার করতে পারবো। আমি আমার মতো করে ফাংশনকে কল করতে পারবো। ফাংশনকে আমরা রিইউজ করতে পারি, কারণ ফাংশনের একটা নাম আছে। কিন্তু লুপের কোনো নাম নেই। সুতরাং লুপকে চাইলে আমি যেখানে সেখানে ইউজ করতে পারবো না। আবার লুপ চালু হলে তাকে আমার হয় ব্রেক করে দিতে হবে, নাহয় লুপ শেষ না হওয়া পর্যন্ত চলতে দিতে হবে। লুপের উপর আমাদের কন্ট্রোল নাই। কিন্তু ফাংশনকে আমরা বিভিন্ন জায়গায় আমাদের প্রয়োজন অনুসারে ব্যবহার করতে পারবো। আমাদের প্রয়োজন অনুসারে কন্ট্রোল করতে পারবো। আগের উদাহরণ থেকে যদি আমি কয়েকটা লাইন নিই + +```js +for (let i = 0; i < allStudents.length; i++) { + // sendEmail(allStudents[i].email); + console.log('Sending email to', allStudents[i].email); +} + +// function sendEmail(email) { +// console.log('Sending email to', email); +// } +``` + +আমরা ফাংশন না লিখেও সেইম কাজ করতে পারতাম। কিন্তু লুপের ভিতরের লাইনটা লুপের বাইরে অন্য কোথাও কাজ করবে না। কিন্তু ফাংশন যেকোনো জায়গায় কল করা যাবে। আবার ধরেন আমার অন্য জায়গায় দরকার পুরো নাম সেটা কি আবার সেই লুপ চালিয়ে করবো। না, আমরা ফাংশন দিয়ে করবো। এক্ষেত্রে আমরা একটা বিল্ড ইন ফাংশন ব্যবহার করে দেখি। + +```js +allStudents.forEach((item) => console.log('Email ', item.email)); +allStudents.forEach((item) => + console.log('Full Name ', item.firstName, item.secondName) +); +``` + +এক্ষেত্রে আমরা বারবার লুপ না চালিয়ে `forEach` ফাংশনটা ব্যবহার করলাম। ফাংশনের সবচেয়ে বড় সুবিধা এটাকে মাল্টিপল টাইম ব্যবহার করা যায় যেকোনো জায়গায়। + +ফাংশন কখন ব্যবহার করবো আর লুপ কখন? যখন একই কাজ আমরা দুইটা ভিন্ন ভিন্ন জায়গায় করবো সেক্ষেত্রে ফাংশন। আর যদি এক জায়গাতেই হয় তাহলে লুপ। যেমন যদি আমরা ইমেইল পাঠাই, আর ইমেইলে পুরো নামের দরকার হয় সেক্ষেত্রে আমরা লুপ দিয়ে কাজ সেরে ফেলতে পারি। কিন্তু যদি আমরা এক জায়গায় ইমেইল পাঠাই, আর অন্য জায়গায় ছাত্র তালিকা তৈরির জন্য পুরো নাম দরকার হয় সেক্ষেত্রে লুপ দিয়ে কাজ হবে না। আমাদের ফাংশন ব্যবহার করতে হবে। এটা প্রাথমিক অবস্থায় বুঝানো অনেক কঠিন হবে। আস্তে আস্তে প্র্যাকটিস করতে থাকলে এটা একসময় মাথায় গেঁথে যাবে। + +ফাংশন লেখার নিয়ম নিচে দেয়া হলোঃ + +```js +function nameOfFunction() { + console.log('Hello', 'Elias'); +} + +nameOfFunction(); // Hello Elias +nameOfFunction(); // Hello Elias +nameOfFunction(); // Hello Elias +``` + +এখন এখানে যতোবারই আমি কল করছি ততোবারই একই আউটপুট দিচ্ছে। বিভিন্ন জায়গায় যদি এই একই আউটপুট দরকার হয় তখন আমরা এভাবে ফাংশন তৈরি করবো। এখন আমি চাইছি ডায়নামিক্যালি নাম বসাবো। যেই নাম আমি ইনপুট দিবো সেটাই বসবে। এর জন্য আমাদের প্যারেন্থেসিসের ভেতর একটা ভ্যারিয়েবল নিতে হবে। একে বলে প্যারামিটার। আমরা একটু দেখি সেটা। + +```js +function nameOfFunction(name) { + console.log('Hello', name); +} + +nameOfFunction('Murshed'); // Hello Murshed +nameOfFunction('Fahim'); // Hello Fahim +nameOfFunction(); // Hello undefined +``` + +আমরা যখন ফাংশন কল করবো তখন আমাদের নামটা আর্গুমেন্ট আকারে সেই প্যারামিটারের জায়গায় বসিয়ে দিবো। প্যারামিটার হলো আপনি ফাংশন লেখার সময় যে ভ্যারিয়েবলটা দিবেন। আর আর্গুমেন্ট হলো ফাংশন কল করার সময় আপনি ভ্যালু আকারে যেটা পাস করবেন। এখন যদি না বসাই তাহলে `undefined` দেখাবে। এখানে আমরা একটা সহজবোধ্য কাজ করে দিতে পারি যদি ইউজার নাম না দেয় তাহলে আমরা তাকে নাম দেয়ার জন্য একটা ম্যাসেজ দেখাতে পারি। মানে একটা এরর হ্যান্ডেল করতে পারি। + +```js +function nameOfFunction(name) { + if (!name) { + console.log('Please provide your name'); + } else { + console.log('Hello', name); + } +} + +nameOfFunction('Murshed'); // Hello Murshed +nameOfFunction('Fahim'); // Hello Fahim +nameOfFunction(); // Please provide your name +``` + +আশা করি এই উদাহরণটা সবাই বুঝতে পারি। আরেকটা উদাহরণ দেখুন কেন ফাংশন আমাদের দরকার। ধরেন আমরা ১ থেকে ১০ এর মধ্যে কোনো র‍্যানডম নাম্বার জেনারেট করতে চাইছি। তাহলে র‍্যান্ডম নাম্বার জেনারেট করার জন্য একটা ফরমুলা লিখে ফেলি। + +```js +const randomNumber = Math.floor(Math.random() * 10); +``` + +এখন যদি ১ থেকে ১০০ এর মধ্যে চাই + +```js +const randomNumber = Math.floor(Math.random() * 100); +``` + +এভাবে লিখতে হবে। যদি এখন ১ থেকে ১০০০ এর মধ্যে চাই + +```js +const randomNumber = Math.floor(Math.random() * 1000); +``` + +এভাবে লিখতে হবে। এখন এতবার না লিখে আমরা এটাকে একটা ফাংশন বানিয়ে ফেলি। + +```js +function generateRandomNumber(max) { + const randomNumber = Math.floor(Math.random() * max); + return randomNumber; +} + +console.log(generateRandomNumber(10)); +console.log(generateRandomNumber(100)); +console.log(generateRandomNumber(1000)); +``` + +এখন আমি যত চাই ততবার র‍্যান্ডম নাম্বার জেনারেট করতে পারবো। এখন আমি চাইছি দুইটা নাম্বারের মধ্যে একটা র‍্যান্ডম নাম্বার জেনারেট করতে। + +```js +function generateRandomNumber(min, max) { + const randomNumber = Math.floor(Math.random() * min + (max - min)); + return randomNumber; +} + +console.log(generateRandomNumber(5, 10)); +console.log(generateRandomNumber(85, 100)); +``` + +তাহলে বুঝুন ফাংশন আমাদের কাজকে কতটা সহজ করে দেয়। + +## Expression vs Statement + +এই বিষয় বুঝার আগে আমরা একটু কিছু উদাহরণ দেখি + +```js +const name1 = 'Rayhan'; // Statement +const name2 = 'Alvi'; // Statement +const name3 = 'Anik'; // Statement +const name4 = 'Arjun'; // Statement +const name5 = 'Ayman'; // Statement + +const students = [ + 'Rayhan', + 'Alvi', + 'Anik', + 'Arjun', + 'Ayman', + 'Ayuub', + 'Bidyut', +]; // Statement + +console.log(students[0]); // Expression +console.log(students[1]); // Expression +console.log(students[2]); // Expression +console.log(students[3]); // Expression +console.log(students[4]); // Expression + +for (let i = 0; i < students.length; i++) { + console.log(students[i], students[i].toLowerCase()); // Expression +} // Statement + +name1.sendEmail(); // Expression +name2.sendEmail(); // Expression +name3.sendEmail(); // Expression +name4.sendEmail(); // Expression +name5.sendEmail(); // Expression + +const nums = [1, 2, 3, 4, 5, 6]; // Statement +const bools = [true, true, false, false]; // Statement +const nulls = [null, null, null]; // Statement +const undefineds = [undefined, undefined, undefined]; // Statement +const arrayOfArray = [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], +]; // Statement +const mixed = [true, null, 'Str', 5, [12, 2, 4]]; // Statement + +const student1 = { + firstName: 'Abu', + secondName: 'Rayhan', + email: 'rayhan@example.com', + age: 25, + attend: true, +}; // Statement + +const student2 = { + firstName: 'Alvi', + secondName: 'Chowdhury', + email: 'alvi@example.com', + age: 25, + attend: true, +}; // Statement + +const student3 = { + firstName: 'Akib', + secondName: 'Ahmad', + email: 'akib@example.com', + age: 25, + attend: true, +}; // Statement + +const allStudents = [student1, student2, student3]; // Statement + +for (let i = 0; i < allStudents.length; i++) { + sendMail(allStudents[i].email); // Expression +} // Statement + +function sendMail(email) { + console.log('Sending email to', email); +} // Statement + +allStudents.forEach((item) => console.log('Email ', item.email)); // Expression +allStudents.forEach((item) => + console.log('Full Name ', item.firstName, item.secondName) +); // Expression + +function nameOfFunction(name) { + if (!name) { + console.log('Please provide your name'); + } else { + console.log('Hello', name); + } +} // Statement + +nameOfFunction('Murshed'); // Expression +nameOfFunction('Fahim'); // Expression +nameOfFunction(); // Expression + +function generateRandomNumber(min = 1, max) { + const randomNumber = Math.floor(Math.random() * min + (max - min)); // Statement + return randomNumber; // Expression +} // Statement + +console.log(generateRandomNumber(5, 10)); // Expression +``` + +Expression and Statement এর মধ্যে বেসিক যে পার্থক্য সেটা হলো এক্সপ্রেশন দিন শেষে কিছু না কিছু রিটার্ন করে, ডাটা প্রোডিউস করে, এবং একে কোনো এক জায়গায় স্টোর করে রাখা যায়। সেই হিসেবে ফাংশন কল এক ধরণের এক্সপ্রেশন। আর স্টেটমেন্ট কোনো ডাটা প্রোডিউস করেনা, কোথাও স্টোর করে রাখা যায় না, কিছু রিটার্ন করে না। ফাংশন লেখা হচ্ছে স্টেটমেন্ট, আর ফাংশন কল হচ্ছে এক্সপ্রেশন। কারণ ফাংশন লিখলে তা কিছু রিটার্ন করে না যতক্ষণ পর্যন্ত কল করা না হচ্ছে। আবার যদি অ্যারো ফাংশন লেখা হয় সেটা এক্সপ্রেশন কারণ সেটাকে একটা ভ্যারিয়েবলে স্টোর করে রাখা হচ্ছে। + +## কোথায় অ্যারে ব্যবহার করবো আর কোথায় অবজেক্ট? + +যে টার্মগুলো Singular সেখানে আমরা ব্যবহার করবো অবজেক্ট। যেখানে Plural সেখানে আমরা ব্যবহার করবো অ্যারে। যেমন একটা ফোন - অবজেক্ট, অনেকগুলো ফোন - অ্যারে, person - object, people - array, member - object, members - array। যেখানে একজন বা একটা কিছুর ইনফরমেশন সেখানে অবজেক্ট। যেখানে একের অধিক লোক বা একের অধিক অবজেক্ট বা বস্তু আসবে সেখানেই আমরা অ্যারে ব্যবহার করবো। জাস্ট এই কথাটা মাথায় রাখবেন। জীবনে আর অ্যারে আর অবজেক্ট নিয়ে গুলিয়ে ফেলবেন না। + +## জাভাস্ক্রিপ্টে ভাল দখল আনার জন্য কোন কোন বিষয়ের উপর জোর দিতে হবে? + +- Arrays +- Objects +- Functions and Functional Programming +- Basic OOP + + + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/docs/Lectures/Fundamentals/04/resource.md b/docs/Lectures/Fundamentals/04/resource.md new file mode 100644 index 0000000..f0faa3b --- /dev/null +++ b/docs/Lectures/Fundamentals/04/resource.md @@ -0,0 +1,243 @@ +# Resource +## Lecture 4 - Programming Fundamentals using JavaScript + +## Programming Fundamentals + +
+ Variables +

Variables helps us to make thing dynamic.

+ +```javascript +const names = [ + 'HM Nayeem', + 'Aditya Chakraborty', + 'Abu Rayhan', + 'Shaker Hossain', + 'Akib Ahmad', + 'Alvi Chowdhury', +]; +let index = -1; +let person = names[++index]; + +setInterval(() => { + person = names[index++]; + console.log(person, person.length); + + if (index === names.length) { + index = 0; + } +}, 1000); +``` + +
+ +
+ Operators +

Mathematical representations

+
+ +
+ Conditions +

Brain of a computer

+ +```javascript +if (studyBasic) { + wontJoin(); +} + +if (studyAdvanced) { + join(); +} + +if (teacherSpeaks) { + silent(); +} + +if (!teacherSpeaks) { + shout(); +} + +// Scenario 1 - Single branch +// if condition +if (hasMoney) { + buyPhone(); +} + +// Scenario 2 - Two branches +// if else condition +if (toss === 'head') { + win(); +} else { + loss(); +} + +// Scenario 3 - Multiple branches +// else if +if (1 > 1) { + big(); +} else if (1 < 1) { + small(); +} else { + same(); +} +``` + +
+ +
+ Loops + +```javascript +for (let i = 1; i <= 100; i++) { + // it's a new js file, + // we can write any valid js code here + // every code written inside this block will execute multiple times + console.log('Hello world!', i); +} + +// There are total three types of loop available in JS +// 1. for (When we know the range) +// 1.1 Range +// 1.2 for in +// 1.3 for of +// 2. while (When we don't know the range) +// 3. do while * + +while (true) { + let num = Math.ceil(Math.random() * 100); + console.log('Hello World', num); + if (num === 99) break; +} + +do { + console.log('It will run at least once'); +} while (false); +``` + +
+ +
+ Arrays + +```javascript +const name1 = 'Rayhan'; +const name2 = 'Alvi'; +const name3 = 'Anik'; +const name4 = 'Arjun'; +const name5 = 'Ayman'; + +const students = [ + 'Rayhan', + 'Alvi', + 'Anik', + 'Arjun', + 'Ayman', + 'Ayuub', + 'Bidyut', +]; + +// console.log(students[0]); +// console.log(students[1]); +// console.log(students[2]); +// console.log(students[3]); +// console.log(students[4]); + +for (let i = 0; i < students.length; i++) { + console.log(students[i], students[i].toLowerCase()); +} + +// name1.sendEmail(); +// name2.sendEmail(); +// name3.sendEmail(); +// name4.sendEmail(); +// name5.sendEmail(); + +const nums = [1, 2, 3, 4, 5, 6]; +const bools = [true, true, false, false]; +const nulls = [null, null, null]; +const undefineds = [undefined, undefined, undefined]; +const arrayOfArray = [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], +]; +const mixed = [true, null, 'Str', 5, [12, 2, 4]]; +``` + +
+ +
+ Objects + +```javascript +const student1 = { + firstName: 'Abu', + secondName: 'Rayhan', + email: 'rayhan@example.com', + age: 25, + attend: true, +}; + +const student2 = { + firstName: 'Alvi', + secondName: 'Chowdhury', + email: 'alvi@example.com', + age: 25, + attend: true, +}; + +const student3 = { + firstName: 'Akib', + secondName: 'Ahmad', + email: 'akib@example.com', + age: 25, + attend: true, +}; + +const allStudents = [student1, student2, student3]; + +for (let i = 0; i < allStudents.length; i++) { + sendMail(allStudents[i].email); +} + +function sendMail(email) { + console.log('Sending email to', email); +} +``` + +
+ +
+ Functions + +```javascript +function nameOfFunction(name) { + if (!name) { + console.log('Please provide your name'); + } else { + console.log('Hello', name); + } +} + +nameOfFunction('Murshed'); +nameOfFunction('Fahim'); +nameOfFunction(); + +function generateRandomNumber(min = 1, max) { + const randomNumber = Math.floor(Math.random() * min + (max - min)); + return randomNumber; +} + +console.log(generateRandomNumber(5, 10)); +``` + +
+ +
+ Expression vs Statement +
+ +### Important Links + +- Blog site - [Hashnode](https://hashnode.com/) +- [Class Overview](../../Class%20Overview/Lecture-04/README.md) diff --git a/docs/Lectures/Fundamentals/05/Overview.md b/docs/Lectures/Fundamentals/05/Overview.md new file mode 100644 index 0000000..d3428c5 --- /dev/null +++ b/docs/Lectures/Fundamentals/05/Overview.md @@ -0,0 +1,1424 @@ +## Lecture 5 - Array Operations - Imperative vs Declarative + +এই দুই লেকচারে আমরা আজ অ্যারে এবং অবজেক্ট নিয়ে বিশদ আলোচনা করবো। যেহেতু এই দুই লেকচার একটার সাথে একটা রিলেটেড তাই আমার কাছে দুইটা লেকচারের ওভারভিউ একসাথে লেখাটা যুক্তিযুক্ত বলে মনে হয়েছে। আমাদের আজকের এজেন্ডা হলো- + + + + + +### Imperative Traverse + +আমাদেরকে যদি বলা হয় একটা অ্যারে ট্রাভার্স করার জন্য, আমরা খুব সহজেই একটা লুপ চালিয়ে ট্রাভার্স করে ফেলতে পারি। এখন প্রশ্ন আসতে পারে ট্রাভার্স কি। ট্রাভার্স হলো ধরুন আমরা একটা অ্যারের প্রতিটা ইলেমেন্ট যেমন লুপ চালিয়ে টাচ করে যে অপারেশন করা দরকার করতে পারি এটাকেই বলে ট্রাভার্স। যেমনঃ + +```js +const numbers = [2, 5, 6, 7, 89, 100]; + +for (let i = 0; i < numbers.length; i++) { + console.log(numbers[i]); +} +``` + +আমরা সব ইলেমেন্ট প্রিন্ট করে ফেলতে পারি এভাবে `numbers` অ্যারের। আমরা যদি চাই প্রতিটা ইলেমেন্ট ২ দ্বারা গুণ করে সেই আউটপুট দেখাবো সেটাও পারবো। + +```js +const numbers = [2, 5, 6, 7, 89, 100]; + +for (let i = 0; i < numbers.length; i++) { + console.log(numbers[i] * 2); +} +``` + +এবার যদি আমরা চাই সব ইলেমেন্টের যোগফল বের করবো তাও পারবো। + +```js +const numbers = [2, 5, 6, 7, 89, 100]; + +let sum = 0; +for (let i = 0; i < numbers.length; i++) { + sum += numbers[i]; +} + +console.log(sum); +``` + +একে বলে Imperative Traversing। কারণ আমরা কোথা থেকে লুপ শুরু হবে তা বলে দিয়েছি, কোথায় গিয়ে থামবে তাও বলে দিয়েছি, এমনকি কিভাবে ইনক্রিমেন্ট হবে তাও বলে দিয়েছি। এরপর অপারেশন কি হবে সেটাও বলে দিয়েছি। তাই এটা একটা Imperative Traversing। + +### Declarative Traverse + +সাধারণত আমাদের ফর লুপ চালিয়ে জাভাস্ক্রিপ্টে কাজ করতে হয় না। যেহেতু জাভাস্ক্রিপ্ট একটা হাই লেভেল ল্যাঙ্গুয়েজ সেহেতু এর বিভিন্ন মেথড আছে, যেগুলো ব্যবহার করে আমরা ডিক্লারেটিভ ওয়েতে ট্রাভার্স করতে পারি। ফাংশন এবং মেথড কি এগুলো আমরা পরবর্তীতে জানবো। আমরা যেভাবে ইম্পারেটিভ ট্রাভার্স করেছিলাম সেভাবে যদি ডিক্লারেটিভ ওয়েতে করতে যায় তাহলে একটা সুন্দর মেথড আছে যার নাম `forEach`। আমরা একটু এই মেথড বুঝার চেষ্টা করি। + +```js +const numbers = [2, 5, 6, 7, 89, 100]; + +numbers.forEach(function () { + console.log('Hello World'); +}); +``` + +এখন এই প্রোগ্রাম রান করালে দেখা যাবে যে ছয়বার `Hello World` প্রিন্ট হবে। কেন ছয়বার কারণ `numbers` এর ইলেমেন্ট আছে ছয়টা। `forEach` এর কাজই হলো যতটা ইলেমেন্ট ততবার লুপ চলবে। `forEach` এর মধ্যে আর্গুমেন্ট আকারে একটা কলব্যাক ফাংশন পাস করবে। আমরা চাইলে ফাংশনটা `forEach` এর মধ্যে না লিখে বাইরে লিখে সেই ফাংশনের নামটাও পাস করে দিতে পারি। এখন ভিতরের ফাংশনটা কিন্তু আমরা কোথাও কল করিনি। তাহলে কিভাবে তা কল হলো? আমাদের জন্য `forEach` সেই ফাংশনটা কল করে রেখেছে কোনো না কোনোভাবে। এই কলব্যাক ফাংশনের মধ্যে প্যারামিটার আকারে কিছু না কিছু আছে। সেগুলো সব `arguments` নামক একটা ডাটা স্ট্রাকচারে স্টোর করা আছে। এটা অনেকটা অ্যারের মতো কাজ করে, কিন্তু অ্যারে না, এটা একটা ডিফারেন্ট টাইপের একটা ডাটা স্ট্রাকচার। একটা উদাহরণ দিলে সুন্দরভাবে বুঝা যাবে ব্যাপারটা। + +```js +const numbers = [2, 5, 6, 7, 89, 100]; + +numbers.forEach(function () { + console.log(arguments); +}); + +/* * Output +[Arguments] { '0': 2, '1': 0, '2': [ 2, 5, 6, 7, 89, 100 ] } +[Arguments] { '0': 5, '1': 1, '2': [ 2, 5, 6, 7, 89, 100 ] } +[Arguments] { '0': 6, '1': 2, '2': [ 2, 5, 6, 7, 89, 100 ] } +[Arguments] { '0': 7, '1': 3, '2': [ 2, 5, 6, 7, 89, 100 ] } +[Arguments] { '0': 89, '1': 4, '2': [ 2, 5, 6, 7, 89, 100 ] } +[Arguments] { '0': 100, '1': 5, '2': [ 2, 5, 6, 7, 89, 100 ] } +*/ +``` + +আউটপুট থেকে দেখা যাচ্ছে অবজেক্টের মধ্যে '0' এর মধ্যে আছে আমাদের অ্যারের প্রতিটা ভ্যালু, '1' এর মধ্যে আছে সেই সংশ্লিষ্ট ভ্যালুর ইনডেক্স নাম্বার এবং '2' এর মধ্যে আছে পুরো অ্যারে। তাহলে আমরা বুঝতে পারলাম, `forEach` এর মধ্যে আর্গুমেন্ট আকারে যে ফাংশনটা আছে তার ভিতর তিনটা প্যারামিটার আছে। যদি একটু আমরা চেক করে দেখি, + +```js +const numbers = [2, 5, 6, 7, 89, 100]; + +numbers.forEach(function (value, index, array) { + console.log(value, index, array); +}); + +/* * Output +2 0 [ 2, 5, 6, 7, 89, 100 ] +5 1 [ 2, 5, 6, 7, 89, 100 ] +6 2 [ 2, 5, 6, 7, 89, 100 ] +7 3 [ 2, 5, 6, 7, 89, 100 ] +89 4 [ 2, 5, 6, 7, 89, 100 ] +100 5 [ 2, 5, 6, 7, 89, 100 ] +*/ +``` + +দেখা যাচ্ছে আমরা যে আউটপুট পেয়েছিলাম আর্গুমেন্টস এর বেলায় ঠিক সেই আউটপুটই পেয়েছি। `arguments` অনেক কাজের। আপনি যখন কোনো লাইব্রেরি বা ফ্রেমওয়ার্ক নিয়ে কাজ করবেন তখন যদি কোনো মেথডের আর্গুমেন্ট জানার প্রয়োজন হয় সহজেই তা বের করে নিতে পারবেন। + +এবার আসি আবার `forEach` এর কথায়। এটা দিয়ে ফর লুপের যাবতীয় যা যা কাজ আমরা করি সবই করতে পারি। এবার আমরা ফর লুপ দিয়ে যোগফলের যে কাজটি করেছিলাম সেটা একটু `forEach` দিয়ে করে দেখি। + +```js +const numbers = [2, 5, 6, 7, 89, 100]; + +let sum = 0; +numbers.forEach(function (value) { + sum += value; +}); +console.log(sum); // 209 +``` + +একই রেজাল্ট পাবো আমরা। এখানে একটা কথা বলে রাখা দরকার, যদি আমাদের `value` ছাড়া আর কিছু না লাগে তবে ফাংশন প্যারামিটার হিসেবে শুধু `value` নিলেই হবে। কিন্তু আমার যদি শুধু `array` দরকার হয় তবে অবশ্যই `value, index, array` এভাবে লিখতে হবে। নাহয় প্রোগ্রাম ভুল আউটপুট দেখাবে। এবার যদি চাই আমরা শুধু জোড় ইলেমেন্টগুলো প্রিন্ট করবো সেটাও পারবো। + +```js +const numbers = [2, 5, 6, 7, 89, 100]; + +numbers.forEach(function (value) { + if (value % 2 === 0) { + console.log(value); + } +}); +``` + +এখানে `forEach` ফাংশন আমরা তৈরি করিনি। আমরা শুধু ব্যবহার করেছি। সুতরাং এটি একটি ডিক্লারেটিভ মেথড। এখন হয়তো অনেকেরই জানতে ইচ্ছা করছে `forEach` মেথডে কি এমন করা হয়েছে। যারা `forEach` সহ অ্যারে এবং অ্যারে মেথড সম্পর্কে জানতে আগ্রহী তারা স্ট্যাক লার্নারের এই [প্লেলিস্ট](https://youtube.com/playlist?list=PL_XxuZqN0xVDr08QgQHljCecWtA4jBLnS) দেখতে পারেন। + +এখন আমি চাইছি যে শুধু প্রথম ৪টা ইলেমেন্টের যোগফল বের করবো। সেটার জন্য আমাদের কি করতে হবে তাহলে? + +```js +const numbers = [2, 5, 6, 7, 89, 100]; + +let sum = 0; +numbers.forEach(function (value, index) { + if (index <= 3) { + sum += value; + } +}); +console.log(sum); +``` + +`forEach` মেথড মনে রাখার সহজ উপায় হলো, আমরা যে ফর লুপ লিখতাম সেটা আর লিখতে হবে না। সেটা `forEach` আমাদের জন্য করে দিয়েছে। শুধু আমাদের কাজ হচ্ছে যেটা আমরা লুপের বডিতে লিখতাম সেটা আমরা কলব্যাক ফাংশনের বডির মধ্যে লিখবো। + +ধরি আমাদের একটা অ্যারে আছে নিচের মতো। + +```js +const arr = [1, 2, 3, null, false, 4, 5, '', 'test', 6, 7]; +``` + +এখন আমরা চাইছি এখান থেকে নাম্বার ছাড়া বাকি যা আছে সেগুলো বাদ দিয়ে শুধু নাম্বারগুলো ফিল্টার করে নিতে। সেটা আমরা ডিক্লারেটিভ ওয়েতে করতে চাইছি না। আমরা চাইছি ইম্পেরেটিভ ওয়েতে করতে। কিভাবে করতে পারি? + +আমরা এভাবে শুরু করতে পারি। + +```js +const arr = [1, 2, 3, null, false, 4, 5, '', 'test', 6, 7]; + +for (let i = 0; i < arr.length; i++) { + if (typeof arr[i] !== 'number') { + arr[i] = undefined; + } +} + +console.log(arr); // [1, 2, 3, undefined, undefined, 4, 5, undefined, undefined, 6, 7]; +``` + +এখন এখানে সমস্যা হলো এই `undefined` গুলোকে কিভাবে আমরা বাদ দিবো। আমাদের অন্য ওয়েতে চিন্তা করতে হবে। আমরা এমন করতে পারি যে কোনো পজিশনে ইলেমেন্ট টাইপ যদি নাম্বার না হয় তাহলে আমরা পরবর্তী ভ্যালুকে অ্যাসাইন করে দিতে পারি। যদি আমরা স্টেপগুলো একটু দেখি তাহলে বোঝা যাবে। + +```js +// step 1: [1, 2, 3, false, 4, 5, '', 'test', 6, 7, undefined] +// step 2: [1, 2, 3, 4, 5, '', 'test', 6, 7, undefined, undefined] +// step 3: [1, 2, 3, 4, 5, 'test', 6, 7, undefined, undefined, undefined] +// step 4: [1, 2, 3, 4, 5, 6, 7, undefined, undefined, undefined, undefined] +``` + +এবার আমাদের আইডিয়াকে আমরা একটু কোডে রূপান্তরিত করে দেখি। + +```js +const arr = [1, 2, 3, null, false, 4, 5, '', 'test', 6, 7]; + +for (let i = 0; i < arr.length; i++) { + for (let j = i; j < arr.length - 1; j++) { + if (!arr[j] || typeof arr[j] !== 'number') { + arr[j] = arr[j + 1]; + arr[j + 1] = undefined; + } + } +} + +console.log(arr); // [1, 2, 3, 4, 5, 6, 7, undefined, undefined, undefined, undefined]; +``` + +আমরা তাহলে আমাদের স্টেপ ৪ পেয়ে গেলাম। এবার এখান থেকে `undefined` বাদ দিয়ে দিতে হবে। সেটার জন্য আমরা একটা কাজ করতে পারি। + +```js +const arr = [1, 2, 3, null, false, 4, 5, '', 'test', 6, 7]; + +count = 0; +for (let i = 0; i < arr.length; i++) { + for (let j = i; j < arr.length - 1; j++) { + if (!arr[j] || typeof arr[j] !== 'number') { + arr[j] = arr[j + 1]; + arr[j + 1] = undefined; + } + } + + if (arr[i] == undefined) { + count++; + } +} +arr.length -= count; + +console.log(arr); // [1, 2, 3, 4, 5, 6, 7]; +``` + +আমরা করেছি কি? যদি ইলেমেন্ট আনডিফাইন্ড হয় তাহলে কাউন্ট করে সেটা `count` ভ্যারিয়েবলের মধ্যে রাখবে। শেষে আমরা `arr.length` থেকে `count` বিয়োগ করে অ্যারের সাইজ কমিয়ে দিলেই `undefined` সব বাদ পড়ে যাবে। + +এবার একটু কোডটা এনালাইসিস করার চেষ্টা করি। আমরা ছোট একটা অ্যারে দিয়েই বুঝার চেষ্টা করি। + +```txt +const arr = [1, false, true, '', 2, 3] +When i = 0: + j = 0: + arr[0] = 1, which is a number + j = 1: + arr[1] = false, which is not a number + so, arr[1] = true + arr[2] = undefined + j = 2: + arr[2] = undefined + so arr[2] = '' + arr[3] = undefined + j = 3: + arr[3] = undefined + so arr[3] = 2 + arr[4] = undefined + j = 4: + arr[4] = undefined + so arr[4] = 3 + arr[5] = undefined + count = 1 +After completion of first loop the array becomes like this [1, true, '', 2, 3, undefined] +After completion of loop the array looks like this [1, 2, 3, undefined, undefined, undefined] and count will be 3. After subtraction count from arr.length (6) we found 3. So the array of length 3 will become like this [1, 2, 3] +``` + +এখন যদি এই কাজটা ইম্পেরেটিভ ওয়েতে না করে ডিক্লারেটিভ ওয়েতে করতাম তাহলে অনেক সহজে করতে পারতাম। + +```js +const arr = [1, 2, 3, null, false, 4, 5, '', 'test', 6, 7]; + +const filteredArray = arr.filter((val) => typeof val === 'number'); +console.log(filteredArray); +``` + +কিন্তু এই জায়গায় একটা সমস্যা আছে। কারণ `filter` মেথড বিহাইন্ড দ্য সীন একটা এক্সট্রা মেমোরি ব্যবহার করে। আমরা যখন ফ্রন্টএন্ড ডেভেলপমেন্ট করি তখন সাধারণত এতো জটিল ইম্পেরেটিভ ওয়েতে করি না। আমরা যে বিল্ট-ইন মেথড আছে সেগুলো ব্যবহার করি। তাই দেখা যায় যে অনেক সময় ডাটা যখন অনেক বেশি হবে তখন অ্যাপ্লিকেশন হ্যাং হয়ে যায়। এখন আমরা কি সবসময় ইম্পেরেটিভ মেথডেই কাজ করবো? বা কখন বুঝবো আমাকে ইম্পেরেটিভ ওয়েতে করতে হবে, কখন ডিক্লারেটিভ ওয়েতে? প্রথম কথা হচ্ছে ৯০-৯৫% সময়ই আমাদের বিল্ট-ইন মেথড ইউজ করে কাজ হয়ে যাবে। কিন্তু কিছু কিছু ক্ষেত্রে আমাদের অ্যাপ্লিকেশনের কমপ্লেক্সিটি এতো বেশি হয় সেসব ক্ষেত্রে আমাদের বিল্ট-ইন মেথডের বাইরে গিয়ে কাজ করতে হতে পারে। ধরেন আমাদের অ্যারেতে এখন জাস্ট নাম্বার, স্ট্রিং এসব ডাটা আছে। কিন্তু যদি এমন হয় যে প্রতিটি ইলেমেন্ট এক একটা জায়ান্ট অবজেক্ট এবং প্রতিটা অবজেক্টের সাইজ প্রায় এক এমবি করে (যদিও এক এমবি ডাটা বানানো অনেক কঠিন, তাও বুঝার সুবিধার্থে উদাহরণ দিচ্ছি), এরকম যদি ১০০ টা অবজেক্ট থাকে তাহলে মোট অ্যারের সাইজ হবে ১০০ এমবি। এখন যদি এই ১০০ এমবি ডাটার অপারেশনের জন্য আমার আরো ১০০ এমবি মেমোরি খরচ হয় তাহলে সেটা অনেক সমস্যা। তাই এই ক্ষেত্রে আমাদের সম্পূর্ণ ইম্পেরেটিভ ওয়েতে গিয়ে কাজ করতে হবে। যদি আমাদের এখানে মেমোরি কনস্ট্রেইন না থাকতো তাহলে আমরা ইম্পেরেটিভ ওয়েতেও অনেক সহজে এই কাজটা করতে পারতাম। + +```jsx +const arr = [1, 2, 3, null, false, 4, 5, '', 'test', 6, 7]; + +const newArr = []; +for (let i = 0; i < arr.length; i++) { + if (typeof arr[i] === 'number') { + newArr.push(arr[i]); + } +} +console.log(newArr); +``` + +ফ্রন্টএন্ড অ্যাপ্লিকেশন বানানোর সময় আমাদের খেয়াল রাখতে হবে একজন ইউজার ৬৪ জিবি র‍্যামের পিসিও ইউজ করতে পারে, আবার ২ জিবি র‍্যামের পিসিও ইউজ করতে পারে। ব্যাকএন্ডে যতো ডাটা থাকবে তার জন্য সার্ভার কস্ট আমি বা আমার কোম্পানি বহন করছে। কিন্তু যখন ব্যাপার ফ্রন্টএন্ডের তখন সেটা পুরোপুরি ইউজার কেন্দ্রিক। আমি চাইবো আমার অ্যাপ্লিকেশন যেন ৬৪ জিবি র‍্যামের পিসি থেকেও ইউজ করার যায়, ২ জিবি র‍্যামের পিসি থেকেও ইউজ করা যায় আবার মোবাইল থেকেও যেন ইউজ করা যায়। তাই অনেক ছোট ছোট বিষয় খেয়াল রেখে ফ্রন্টএন্ড ডেভেলপমেন্ট করতে হয়। এখানেই ফ্রন্টএন্ড ডেভেলপমেন্টের চ্যালেঞ্জ। + +> **অ্যারের পরবর্তী ধাপগুলো ভালভাবে বুঝার জন্য অবজেক্ট সম্পর্কে জানা থাকতে হবে। তাই আগে [Object Operations](#object-operations), [Function vs Method](#function-vs-method), [Array](#array), [Object Over Array](#object-over-array), [Comparison of object and array operation costs](#comparison-of-object-and-array-operation-costs) এই টপিকগুলো ভালভাবে পড়ে নিন। এরপর পরবর্তী ধাপে যান।** + +### Update + +আপডেটের ক্ষেত্রে ইম্পেরেটিভ ওয়েতে করার কোনো প্রয়োজন নেই। আপডেট অনেক সিম্পল। আমাদের যদি কোনো অ্যারের ইনডেক্স জানা থাকে তাহলে খুব সহজেই আমরা তার ডাটা আপডেট করে ফেলতে পারি। যেমন + +```js +const arr = [1, 2, 3, 4, 5]; + +arr[3] = 300; + +console.log(arr); // [1, 2, 3, 300, 5] +``` + +এখন যদি ইনডেক্স জানা না থাকে তাহলে প্রথমে আগে ইনডেক্স বের করে নিতে হবে। এরপর আপডেট করা যাবে। যেমন + +```js +const arr = [ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 4, value: 40 }, + { id: 5, value: 50 }, +]; + +const index = arr.findIndex((item) => item.id === 4); +arr[index].value = 400; + +console.log(arr); + +// [ +// { id: 1, value: 10 }, +// { id: 2, value: 20 }, +// { id: 3, value: 30 }, +// { id: 4, value: 400 }, +// { id: 5, value: 50 } +// ] +``` + +আবার ইনডেক্স বের না করেও আপডেট করা যায়। সেক্ষেত্রে আমাদের `find` মেথড ব্যবহার করতে হবে। যেমন + +```js +const arr = [ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 4, value: 40 }, + { id: 5, value: 50 }, +]; + +const obj = arr.find((val) => val.id === 4); +obj.value = 400; + +console.log(obj); // { id: 4, value: 400 } +console.log(arr); + +/* +[ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 4, value: 400 }, + { id: 5, value: 50 } +] +*/ +``` + +এখানে দেখা যাচ্ছে আমি যদি `obj` এর ভ্যালু পরিবর্তন করি তাহলে `arr` এর ভ্যালুও পরিবর্তন হবে। কারণ হলো আমরা এখানে যেভাবে অ্যারে দেখতে পাচ্ছি আসলে তা সেরকম নাই। আমরা যতোই ডাটা রাখি অ্যারেতে জাস্ট অ্যারের মধ্যে কয়েকটা অ্যাড্রেস থাকে। ঐ ডাটাগুলোর অ্যড্রেস। মানে ঐ ডাটাগুলো যে অ্যাড্রেসে থাকে তা অ্যারে ভ্যারিয়েবলের মধ্যে জমা থাকে। আমরা যখন `obj` এর মধ্যে ফাইন্ড করছি তখন অ্যারের ঐ অ্যাড্রেসকে নিয়ে আসছি। তাই অ্যাড্রেস যেখানেই চেইঞ্জ করি না কেন তা অরিজিনাল অ্যারেতেও চেইঞ্জ হয়ে যাচ্ছে। এটা হচ্ছে মিউটেশন। আর `find` মেথড মিউটেবল। + +এখন একটা উদাহরণ দেখি। + +```js +const arr = [ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 4, value: 40 }, + { id: 5, value: 50 }, +]; + +const obj = arr.find((val) => val.id === 4); +obj.value = 400; + +console.log(arr[3] === obj); // true + +const a = { a: 10 }; +const b = { a: 10 }; +const c = a; +console.log(a === c); // true +console.log(a === b); // false +``` + +যেকোনো বিগিনারের কাছে এটা পুরাই কনফিউশন সৃষ্টি করবে। যখন obj কিছু find করে নিয়ে আসে তখন আসলে অ্যারের রেফারেন্সটা নিয়ে আসে। তাই obj এবং arr[3] এর রেফারেন্স একই এজন্যই সেটা `true` আউটপুট দিয়েছে। একই ভাবে c আর a রেফারেন্স একই। তাই সেটা true দিয়েছে। কিন্তু a আর b এর রেফারেন্স সম্পূর্ণ আলাদা। দুইটা অবজেক্টে যতই সেইম ভ্যালু থাক, দুইটা অবেজক্টের রেফারেন্স কখনও এক হবে না। দুইটা বিল্ডিং দেখতে যতোই একই হোক, দুইটা বিল্ডিং এর অ্যাড্রেস কখনও একই হবে না। এক্ষেত্রেও তাই। এই কারণে শেষের কন্ডিশন false দিয়েছে। + +### Delete + +এবার আমরা অ্যারে থেকে কিভাবে কোনো ডাটা ডিলিট করতে হয় তা দেখবো। ইম্পেরেটিভ ওয়েতে কিভাবে ডাটা ডিলিট করতে হয় তা আমরা গেই দেখেছি অ্যারে ট্রাভার্সের উদাহরণে। এখানে আমরা দুইটা মেথড ইউজ করে ডিলিট করবো। `splice` and `filter`. এদের মধ্যে পার্থক্য হলো splice মেথড মিউটেবল এবং filter ইমমিউটেবল। কিভাবে আমরা একটু দেখি। + +```js +const arr = [ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 4, value: 40 }, + { id: 5, value: 50 }, +]; + +const index = arr.findIndex((item) => item.id === 4); +const arr1 = arr.splice(index, 1); + +console.log(arr1); // [ { id: 4, value: 40 } ] +console.log(arr); +/* [ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 5, value: 50 } +] */ +``` + +এখানে দেখা যাচ্ছে splice মেথড সরাসরি অরিজিনাল অ্যারে থেকে ডাটা ডিলিট করে দিয়েছে। তার মানে এখানে মিউটেশন হয়েছে। + +```js +const arr = [ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 4, value: 40 }, + { id: 5, value: 50 }, +]; + +const arr2 = arr.filter((item) => item.id !== 4); + +console.log(arr2); +/* +[ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 5, value: 50 } +] +*/ +console.log(arr); +/* +[ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 4, value: 40 }, + { id: 5, value: 50 } +] +*/ +``` + +এখানে অরিজিনাল অ্যারে যেমন ছিল তেমনই আছে। কিন্তু ফিল্টার করার পর ফিল্টার মেথড নতুন একটা অ্যারে দিয়েছে যেখানে যেটা ডিলিট করতে চেয়েছিলাম সেটা নেই। তার মানে দাঁড়ালো filter মেথড ইমমিউটেবল। + +### Mutation + +মিউটেশন নিয়ে অলরেডি আলোচনা হয়েছে। আশা করি ব্যাপারটা সবাই বুঝতে পেরেছেন। + +### Map + +ম্যাপ সাধারণত অরিজিনাল অ্যারের ক্লোন ভার্সন তৈরি করে। যদি অরিজিনাল অ্যারেতে ১০টা ডাটা থাকে তাহলে নতুন অ্যারেতেও ১০টা ডাটা থাকবে। এখন সে ডাটা একই হতে পারে বা ডিফারেন্ট হতে পারে। যেমন + +```js +const numbers = [1, 2, 3, 4]; +const strs = numbers.map((v) => v.toString()); +console.log(strs); +``` + +সব নাম্বারের স্ট্রিং ভার্সন সে আউটপুট দিবে। একটা জিনিস মাথায় রাখতে হবে ম্যাপ করার পর অ্যারের লেংথের কোনো পরিবর্তন হবে না। শুধু ডাটা পরিবর্তন হবে। ডাটার সংখ্যা একই থাকবে। + +### Filter + +ফিল্টারের কাজ আমরা একটা অ্যারে থেকে যে যে ডাটা চাইছি তা ফিল্টার করে দেয়া। ধরেন আমাদের কাছে একটা অ্যারে আছে। + +```js +const numbers = [1, 2, 3, 4, false, '', NaN, 5, 6]; +``` + +আমরা চাইছি এখান থেকে সমস্ত falsy value বাদ দিয়ে শুধু truthy ভ্যালু নিবো। সেক্ষেত্রে ফিল্টার মেথড আমাদের ব্যবহার করতে হবে। + +```js +const filteredArr = numbers.filter((v) => v); +console.log(filteredArr); +``` + +এক্ষেত্রে সকল truthy value রিটার্ন করে দিবে। কিন্তু এমন কিছু সিচুয়েশন আসবে যখন আমি truthy value চাইছি কিন্তু রিটার্ন করতে পারবো না সেক্ষেত্রে v এর আগে দুইটা !! বসিয়ে দিলেই truthy value পেয়ে যাবো। + +### Reduce + +আমরা একটু নিচের উদাহরণটা দেখি। + +```js +const numbers = [1, 2, 3, 4, false, '', NaN, 5, 6]; +const filteredArr = numbers.filter((v) => v); +const strs = filteredArr.map((v) => v.toString()); +console.log(strs); +``` + +এক্ষেত্রে কিছু অসুবিধা আছে। যখন ফিল্টার হচ্ছে তখন n সংখ্যকবার সে ট্রাভার্স হচ্ছে। আবার যখন ম্যাপ হচ্ছে তখনও আবার ট্রাভার্স হচ্ছে। এতে করে টাইম কমপ্লেক্সিটি বেড়ে যাচ্ছে। এটা চেইন করেও করা যায়। + +```js +const numbers = [1, 2, 3, 4, false, '', NaN, 5, 6]; +const filteredArr = numbers.filter((v) => v).map((v) => v.toString()); +console.log(strs); +``` + +এক্ষেত্রে টাইম কমপ্লেক্সিটি কিছুটা কমলেও পুরোপুরি এফিশিয়েন্ট না। সেজন্য আমাদের যেতে হবে reduce মেথডের কাছে। + +ইউটিউবে আমরা যে সকল টিউটোরিয়াল দেখতে পাই তাতে reduce দিয়ে একটা কাজই ঘুরেফিরে করা হয়। সেটা হলো যোগ করা। + +```jsx +const numbers = [1, 2, 3, 4, 5, 6]; +const sum = numbers.reduce((a, b) => a + b); +console.log(sum); +``` + +কিন্তু reduce is way more powerful than summation. reduce এত পাওয়ারফুল যে তা কল্পনা করা যায় না। reduce ঠিকমতো বুঝলে ম্যাপ, ফিল্টার নিয়ে কাজ না করে reduce নিয়েই কাজ করে ফেলা যায়। ম্যাপ আমাদের রিটার্ন করে একই দৈর্ঘ্যের একটা নতুন অ্যারে। ফিল্টার ফিল্টারড ভ্যালুর অ্যারে রিটার্ন করে। এর দৈর্ঘ্য অরিজিনাল অ্যারের সমান হতেও পারে, নাও পারে। কিন্তু রিডিউস কি যে রিটার্ন করবে তা কেউ জানে না। শুধু আমরা জানবো। এখানে স্ট্রিং, নাম্বার, বুলিয়ান ইত্যাদি যেকোনো সম্ভাব্য ভ্যালুই এটা রিটার্ন করতে পারে। + +আমরা একটু reduce এর স্ট্রাকচারটা দেখি + +```js +numbers.reduce((acc, cur) => { + return acc; +}, ''); +``` + +প্রথম প্যারামিটার হিসেবে আমরা দিয়েছি acc (accumulator / previous value) এবং দ্বিতীয় ভ্যালু হিসেবে দিয়েছি cur (current value)। acc, cur এর পর আমরা চাইলে ইনডেক্স দিতে পারি, চাইলে পুরো অ্যারে দিতে পারি কিন্তু আমাদের সেটা দরকার নেই। reduce মেথডের সুবিধা হলো এখানে আমরা একটা ইনিশিয়াল ভ্যালু প্রোভাইড করতে পারি। '' এর জায়গায় খালি অবজেক্ট {}, খালি অ্যারে [], শূন্য যেকোনো কিছু বসাতে পারি। সেটা আমরা কি চাইছি তার উপর নির্ভর করবে। এর মানে হলো বর্তমানে acc এর ভ্যালু ঐ ইনিশিয়ালাইজার হিসেবে যেটা দিবো সেটা। দিন শেষে আমরা আমাদের acc কে রিটার্ন করবো। যাই করি না কেন আমরা reduce মেথডে acc কেই রিটার্ন করবো। এখন আমরা চাইছি `const numbers = [1, 2, 3, 4, false, '', NaN, 5, 6];` এটা থেকে আমরা `1234falseNaN56` রিটার্ন করতে। সেটা করতে আমরা reduce মেথডের সাহায্য নিবো। + +```js +const numbers = [1, 2, 3, 4, false, '', NaN, 5, 6]; +const result = numbers.reduce((acc, cur) => { + acc += cur.toString(); + return acc; +}, ''); + +console.log(result); // 1234falseNaN56 +``` + +আমরা করেছি কি এখানে? acc এর ভ্যালু আমরা ধরে নিয়েছি ''। এরপর ওটার সাথে cur এর toString যোগ করে দিয়েছি। এবং আমাদের রেজাল্টটাকে আমরা একটা ভ্যারিয়েবলের মধ্যে রেখেছি। এরপর যখন আউটপুট দিলো দেখা গেলো আমরা যেটা চাইছি সেটাই পেয়ে গেছি। + +এখন আমরা চাইছি এই অ্যারে থেকে শুধু truthy values নিবো। কোনো falsy ভ্যালু নিবো না। সেক্ষেত্রে আমরা একটা কন্ডিশন বসিয়ে দিতে পারি। + +```js +const numbers = [1, 2, 3, 4, false, '', NaN, 5, 6]; +const result = numbers.reduce((acc, cur) => { + if (cur) { + acc += cur.toString(); + } + return acc; +}, ''); + +console.log(result); // 123456 +``` + +আমরা যদি চাই প্রতিটার শেষে কমা (,) যোগ করবো সেটাও করতে পারি। + +```js +const numbers = [1, 2, 3, 4, false, '', NaN, 5, 6]; +const result = numbers.reduce((acc, cur, index) => { + if (cur) { + acc += cur.toString() + (index < numbers.length - 1 ? ', ' : ''); + } + return acc; +}, ''); + +console.log(result); // 1, 2, 3, 4, 5, 6 +``` + +আমরা চাইলে অ্যারের একটা শেইপও দিতে পারি। যেমন + +```js +const numbers = [1, 2, 3, 4, false, '', NaN, 5, 6]; +const result = numbers.reduce((acc, cur, i) => { + if (i === 0) { + acc += '['; + } + if (cur) { + acc += cur.toString() + (i < numbers.length - 1 ? ', ' : ''); + } + if (i === numbers.length - 1) { + acc += ']'; + } + return acc; +}, ''); +console.log(result); // [1, 2, 3, 4, 5, 6] +``` + +তাহলে আমরা reduce এর পাওয়ারটা বুঝতে পারছি কিছুটা। এটা গেলো এক ধরণের পাওয়ার। আরো অনেক পাওয়ার আছে reduce মেথডের। যেমন এখন আমরা acc স্ট্রিং হিসেবে চাইছি না। আমরা চাইছি সকল truthy ভ্যালুর একটা অ্যারে। সেটাও reduce দিয়ে করা যায়। + +```js +const numbers = [1, 2, 3, 4, false, '', NaN, 5, 6]; +const result = numbers.reduce((acc, cur) => { + if (cur) { + acc.push(cur.toString()); + } + return acc; +}, []); +console.log(result); // [ '1', '2', '3', '4', '5', '6' ] +``` + +এখানে আমরা acc হিসেবে একটা ফাঁকা অ্যারে নিয়েছি। এরপর একটা কন্ডিশন লিখেছি truthy ভ্যালু পাওয়ার জন্য। তারপর সেই কন্ডিশন যে সকল ভ্যালুর পূরণ করবে তাদের toString ভার্সন আমরা acc এর মধ্যে push করে দিবো যেহেতু acc একটা অ্যারে। আমরা একই রেজাল্ট পাচ্ছি আরো বেটার সল্যুশনের মাধ্যমে। + +আমরা একটু map/filter অপারেশনের সাথে reduce অপারেশনের টাইম কমপ্লেক্সিটি তুলনা করে দেখি। + +```js +const arr = []; +for (let i = 1; i < 5000000; i++) { + arr.push(i); +} + +console.time('not-optimized'); +arr.filter((item) => item % 2 === 0).map((item) => item * 2); +console.timeEnd('not-optimized'); // not-optimized: 325.853ms + +console.time('optimized'); +arr.reduce((acc, cur) => { + if (cur % 2 === 0) { + acc.push(cur * 2); + } + return acc; +}, []); +console.timeEnd('optimized'); // optimized: 198.256ms +``` + +তাহলে দেখা যাচ্ছে reduce method অনেক অপটিমাইজড। এবার আমরা একটু reduce মেথডের ইমপ্লিমেন্টেশনটা দেখি। আমরা আমদের reduce ফুঞ্চতিওন বানিয়ে ফেলতে পারি। যেহেতু আমরা প্রোটোটাইপ নিয়ে আলোচনা করিনি তাই মেথড বানাবো না। আমরা জাস্ট একটা ফাংশন বানাবো। + +```js +function myReduce(arr, cb, init) { + let acc = init; + for (let i = 0; i < arr.length; i++) { + acc = cb(acc, arr[i], i, arr); + } + return acc; +} +``` + +এটাই আমাদের reduce ফাংশন। এখানে কি করেছি একটু ব্যাখ্যা করা যাক। আমরা তিনটা প্যারামিটার নিয়েছি। প্রথম প্যারামিটার হিসেবে থাকবে একটা অ্যারে। দ্বিতীয় প্যারামিটার হিসেবে থাকবে একটা কলব্যাক ফাংশন। আর তৃতীয় প্যারামিটার হিসেবে থাকবে আমাদের ইনিশিয়ালাজার। আমরা যে ইনিশিয়ালাইজার ব্যবহার করেছিলাম reduce মেথডে সেটা। এখন আমরা আমাদের acc হিসেবে init নিয়ে নিলাম। এরপর লুপ চালালাম। লুপের মধ্যে acc আপডেট হচ্ছে কলব্যাক ফাংশন অনুযায়ী। সেই কলব্যাক ফাংশনের প্যারামিটার হিসেবে থাকছে acc, অ্যারের ইলেমেন্ট, ইনডেক্স আর আমাদের অ্যারে। আর এই ফাংশন রিটার্ন করবে আমাদের acc। এবার একটু আমাদের ফাংশনটা টেস্ট করে দেখি। + +```js +const sum = myReduce([1, 2, 3, 4], (a, b) => a + b, 0); +console.log(sum); // 10 + +const arr = [1, 2, '', false, 3, NaN, false, 4, 5, NaN, 6]; +const result = myReduce( + arr, + (acc, cur) => { + if (cur) { + acc.push(cur ** 2); + } + return acc; + }, + [] +); +console.log(result); // [1, 4, 9, 16, 25, 36] +``` + +How amazing is this! জাভাস্ক্রিপ্টের যতোই গভীরে যাবেন এর মজাটা ততোই পাবেন। আমরা আমাদের reduce ফাংশন বানিয়ে সেটা নিয়ে কাজও করে ফেললাম। আর এটাও জানলাম behind the scene redcuce মেথড কিভাবে কাজ করে। + +আমরা আরেকটা উদাহরণ দেখি reduce এর। তার জন্য আমাদের axios প্যাকেজটা ইনস্টল করে নেয়া লাগবে। আমরা ইনস্টল করে নিলাম। এখন আমরা [json placeholder](https://jsonplaceholder.typicode.com/posts) এই সাইটে ঢুকলে কিছু ডামী ডাটা পাবো পোস্টের। খেয়াল করলে দেখবো এই ডাটা দেয়া আছে অ্যারে হিসেবে। কিন্তু আমার ট্রাভার্সের চেয়ে গুরুত্বপূর্ণ হলো আপডেট ও ডিলিট করা। ব্যাকএন্ড ডেভেলপার তার সুবিধামতো অ্যারেতে দিয়ে দিলেও আমাদের নিজেদের প্রয়োজনে তা অবজেক্টে রূপান্তরিত করে নেয়া লাগবে। এখানে আমাদের body প্রোপার্টিজ প্রয়োজন নেই। আমাদের দরকার userId, id ও title। আর আমার এতো ডাটার প্রয়োজন নেই আমাদের প্রথম ১০টা ডাটা হলেই হয়ে যাবে। চলুন দেখি। + +```js +const axios = require('axios').default; +const url = 'https://jsonplaceholder.typicode.com/posts'; + +async function getData() { + const { data } = await axios.get(url); + const result = data.slice(0, 10).map((item) => { + return { + userId: item.userId, + id: item.id, + title: item.title, + }; + }); + return result; +} + +getData() + .then((data) => console.log(data)) + .catch((e) => console.log(e)); + +/* +[ + { + userId: 1, + id: 1, + title: + 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit', + }, + { userId: 1, id: 2, title: 'qui est esse' }, + { + userId: 1, + id: 3, + title: 'ea molestias quasi exercitationem repellat qui ipsa sit aut', + }, + { userId: 1, id: 4, title: 'eum et est occaecati' }, + { userId: 1, id: 5, title: 'nesciunt quas odio' }, + { userId: 1, id: 6, title: 'dolorem eum magni eos aperiam quia' }, + { userId: 1, id: 7, title: 'magnam facilis autem' }, + { userId: 1, id: 8, title: 'dolorem dolore est ipsam' }, + { + userId: 1, + id: 9, + title: 'nesciunt iure omnis dolorem tempora et accusantium', + }, + { userId: 1, id: 10, title: 'optio molestias id quia eum' }, +]; +*/ +``` + +আমরা map ব্যবহার করে প্রথম ১০টি ডাটা পেয়ে গেলাম। এবং বডিও আমরা বাদ দিয়ে দিলাম। কিন্তু এখনও এটা অ্যারে রিটার্ন করছে। map করলে কখনও আমরা অবজেক্ট রিটার্ন করতে পারবো না। কারণ map সবসময় অ্যারেই রিটার্ন করে। এবার আমরা একটু reduce নিয়ে কাজ করি। কারণ reduce এ আমরা কি টাইপের ডাটা চাই তা ইনিশিয়ালাইজের মাধ্যমে দিয়ে দিতে পারি। + +```js +const axios = require('axios').default; +const url = 'https://jsonplaceholder.typicode.com/posts'; + +async function getData() { + const { data } = await axios.get(url); + const result = data.slice(0, 10).reduce((acc, cur) => { + acc[cur.id] = { + ...cur, + }; + delete acc[cur.id].body; + return acc; + }, {}); + return result; +} + +getData() + .then((data) => console.log(data)) + .catch((e) => console.log(e)); + +/* +{ + '1': { + userId: 1, + id: 1, + title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit' + }, + '2': { userId: 1, id: 2, title: 'qui est esse' }, + '3': { + userId: 1, + id: 3, + title: 'ea molestias quasi exercitationem repellat qui ipsa sit aut' + }, + '4': { userId: 1, id: 4, title: 'eum et est occaecati' }, + '5': { userId: 1, id: 5, title: 'nesciunt quas odio' }, + '6': { userId: 1, id: 6, title: 'dolorem eum magni eos aperiam quia' }, + '7': { userId: 1, id: 7, title: 'magnam facilis autem' }, + '8': { userId: 1, id: 8, title: 'dolorem dolore est ipsam' }, + '9': { + userId: 1, + id: 9, + title: 'nesciunt iure omnis dolorem tempora et accusantium' + }, + '10': { userId: 1, id: 10, title: 'optio molestias id quia eum' } +} +*/ +``` + +আমরা এখানে acc হিসেবে নিয়েছি একটা ফাঁকা অবজেক্ট ({})। সেই অবজেক্টের কী হিসেবে থাকবে current ভ্যালুর আইডি। আমরা সেই আইডি ধরে সব current ভ্যালু অবজেক্টে স্টোর করে দিলাম। এখন আমরা তো বডি চাই না। তাই পরের লাইনে সিমপ্লি delete এর মাধ্যমে বডি ডিলিট করে দিলাম। আর দিনশেষে তো acc ই রিটার্ন করবে। সব শেষে যখন রান করালাম, ওয়াও, আমাদের অবজেক্ট আমরা পেয়ে গেলাম। reduce এর পাওয়ার অন্য লেভেলের। এর পাওয়ার বলে শেষ করা যায় না। + +লাস্ট আরেকটা এক্সাম্পল আমরা দেখি এই reduce মেথডের। ধরুন আমাদের কাছে একটা অ্যারে আছে নামের। + +```js +const names = [ + 'Ayman', + 'Abu Rayhan', + 'Anik', + 'Elias Emon', + 'Engr. Sabbir', + 'Fahim Faisal', + 'Feroz Khan', + 'Habib', + 'HM Azizul', + 'Hridoy Saha', + 'Jahid Hassan', + 'Johir', + 'Md Al-Amin', + 'Md Arafatul', + 'Md Ashraful', + 'Parvez', +]; +``` + +আমরা এটাকে নিচের মতো করে আউটপুট পেতে চাইছি। + +```txt +----------- A ----------- +Ayman +Abu Rayhan +Anik + +----------- E ----------- +Elias Emon +Engr. Sabbir + +----------- F ----------- +Fahim Faisal +Feroz Khan + +----------- H ----------- +Habib +HM Azizul +Hridoy Saha + +----------- J ----------- +Jahid Hassan +Johir + +----------- M ----------- +Md Al-Amin +Md Arafatul +Md Ashraful + +----------- P ----------- +Parvez +``` + +এটা আমরা কিভাবে পেতে পারি। আমাদের আছে অ্যারে। আমরা যদি এই কাজটাকে নিচের স্ট্রাকচার হিসেবে কল্পনা করি তাহলে অনেক সহজ হয়ে যাবে। + +```js +const namesGroup = { + A: ['Ayman', 'Abu Rayhan', 'Anik'], + E: ['Elias Emon', 'Engr. Sabbir'], + F: ['Fahim Faisal', 'Feroz Khan'], +}; +``` + +এখন অ্যারে থেকে আমাদের এভাবে অবজেক্টে পরিণত করতে হবে। আর এই কাজটা করতে পারে reduce. তাহলে চলুন করা যাক। + +```js +const namesGrouped = names.reduce((acc, cur) => { + const firstLetter = cur[0].toUpperCase(); + if (firstLetter in acc) { + acc[firstLetter].push(cur); + } else { + acc[firstLetter] = [cur]; + } + return acc; +}, {}); +console.log(namesGrouped); + +/* +{ + A: [ 'Ayman', 'Abu Rayhan', 'Anik' ], + E: [ 'Elias Emon', 'Engr. Sabbir' ], + F: [ 'Fahim Faisal', 'Feroz Khan' ], + H: [ 'Habib', 'HM Azizul', 'Hridoy Saha' ], + J: [ 'Jahid Hassan', 'Johir' ], + M: [ 'Md Al-Amin', 'Md Arafatul', 'Md Ashraful' ], + P: [ 'Parvez' ] +} +*/ +``` + +আমরা প্রথমে আমাদের acc কে একটা ফাঁকা অবজেক্ট হিসেবে নিয়ে নিলাম। এরপর আমরা প্রথম লেটার ধরে চেক করবো তা acc তে আছে কিনা। যদি থাকে কি করবো আর না থাকলে কি করবো। তাহলে প্রথমে আমরা current ভ্যালুর প্রথম লেটারের আপারকেইস নিয়ে একটা ভ্যারিয়েবলে স্টোর করে রাখলাম। এবার একটা কন্ডিশন লিখলাম। যদি firstLetter acc এর মধ্যে না থাকে firstLetter দিয়ে একটা কী তৈরি করবে এবং ঐ কী এর মধ্যে current ভ্যালুর একটা অ্যারে নিবে। যদি firstLetter acc এর মধ্যে থাকে তাহলে জাস্ট কারেন্ট ভ্যালুর যে অ্যারে তাতে push করে দিবে। এবার যদি আমরা একটু আউটপুট দেখি তাহলে দেখবো আমরা যে স্ট্রাকচারটা কল্পনা করেছিলাম সেটা পেয়ে গেছি। এবার এখান থেকে আমাদের রিকোয়ার্ড আউটপুট কিভাবে প্রিন্ট করবো, যেটা শুরুতে দেখিয়েছিলাম, তা একটু দেখি। + +```js +Object.keys(namesGrouped).forEach((groupKey) => { + console.log('-----------', groupKey, '-----------'); + namesGrouped[groupKey].forEach((name) => console.log(name)); + console.log(); +}); +``` + +এটা আশা করি বুঝানোর কিছু নেই। সিম্পল forEach মেথড যা আগে দেখেছিলাম। রান করলে দেখবেন আমাদের ডিজায়ার্ড আউটপুট আমরা পেয়ে গেছি। + +যদি আমাদের filter, map, reduce জানা থাকে ভালভাবে তাহলে অন্যান্য ডাটা স্ট্রাকচার এবং অ্যালগরিদম ব্যবহার না করেও আমরা কিছু কিছু ক্ষেত্রে অপটিমাইজড অ্যাপ্লিকেশন বানিয়ে ফেলতে পারবো। + +## Object Deep Dive + +### Object Operations + +আমাদের চারপাশে আমরা যা দেখি তাই অবজেক্ট। ধরে আমাদের সামনে একটি মাইক্রোফোন আছে। এটাও একটা অবজেক্ট। কিভাবে চলুন একটু দেখি। + +```js +const microphone = { + brand: 'Fifine', + indictor: true, + price: 8000, + color: 'Black', + startRecording() { + console.log('recording started'); + }, + stopRecording() { + console.log('recording stopped'); + }, +}; +``` + +যখন আমাদের কোনো একটা বিষয় বা বস্তুকে রিপ্রেজেন্ট করার জন্য একাধিক ইনফরমেশন দরকার, তখনই আমাদের প্রয়োজন অবজেক্ট। একটা ইনফরমেশন হলে আমরা ভ্যারিয়েবল নিয়ে কাজ সেরে ফেলতে পারতাম। কিন্তু যেহেতু একের অধিক তাই আমাকে ঐ বিষয় বা বস্তু রিপ্রেজেন্ট করার জন্য প্রয়োজন অবজেক্ট। সেইম জিনিস জাভাতে বলে ক্লাস, পাইথনে বলে ডিকশনারি, সি তে সেটা হলো স্ট্রাকচার। এখন অবজেক্ট মানেই অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিং না। অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিং হলো এই অবজেক্টকেই কিভাবে সুন্দর করে অর্গানাইজড ওয়েতে রিপ্রেজেন্ট করা যায় সেটার থিওরাম হচ্ছে অবজেক্ট অরিয়েন্টেড প্রোগ্রামিং। এই টার্মটা আমাদের এখন প্রয়োজন নেই। আমরা বেসিক অবজেক্ট নিয়ে কথা বলছি, তাই ফোকাসটা আপাতত অবজেক্টের দিকেই দিই। + +আমরা জানি যে, অবজেক্টের মধ্যে এর অনেকগুলো প্রোপার্টি রাখতে পারি। অবজেক্টের প্রোপার্টিজকে দুই ভাগে ভাগ করা যায়। যথাঃ + +1. Noun / Adjective (State/data/property/field) - যে প্রোপার্টি দ্বারা আমাদের ডাটা রিপ্রেজেন্ট করতে পারি সেগুলোই এর আলোচ্য বিষয়। উপরের উদাহরণে brand, indicator, price, color এগুলো সবই প্রোপার্টি। কারণ এগুলো ডাটা রিপ্রেজেন্ট করছে। এই ডাটাগুলো স্ট্রিং, নাম্বার, বুলিয়ান যেকোনো ডাটা টাইপের হতে পারে। +2. Verb (functionalities -> start, stop) - যেমন আমাদের মাইক্রোফোনে কিছু ফাংশনালিটিজ থাকে। যেমন, start button, stop button, recording button etc. যেমন উপরের উদাহরণে startRecording, stopRecording। + +তাহলে অবজেক্টের দুইটা অংশের একটা আমাদের ডাটাকে রিপ্রেজেন্ট করবে, আরেকটা অংশ ডাটার সাথে রিলেটেড কাজগুলো করবে। এই দুইটা অংশ মিলেই আমাদের একটা অবজেক্ট তৈরি হয়। + +এখন এখানে যেসব প্রোপার্টি আমরা লিখলাম এর বাইরেও অনেক প্রোপার্টি আছে যেগুলো হিডেন আছে। যেমন আমরা যদি লিখি `microphone.toString()` তাহলে আউটপুট আসবে `[object Object]`। কিন্তু `toString` মেথড তো আমরা এখানে কোথাও লিখিনি। তাহলে এটা আসলো কোথা থেকে। এটা এসেছে `Object` থেকে। এই `Object` কে বলা হয় অবজক্ট কন্সট্রাকটর। + +আমরা যেভাবে অবজেক্ট তৈরি করেছিলাম সেটা ছাড়াও অন্যভাবে অবজেক্ট তৈরি করা যায়। আমরা একটু সেই প্রসেসটাও দেখি। + +```js +const testObj = new Object(); +testObj.name = 'Test Object'; +testObj.time = new Date(); +console.log(testObj); // { name: 'Test Object', time: 2022-06-16T07:09:01.373Z } +``` + +আউটপুটে আমরা দেখতে পাচ্ছি একটা অবজেক্ট ক্রিয়েট হয়ে গিয়েছে। তার মানে আমরা দুইভাবেই অবজেক্ট ক্রিয়েট করতে পারি। প্রথমে যেভাবে তৈরি করেছি সেটাকে বলে `Object Literal` এবং পরে যেভাবে তৈরি করেছি সেটাকে বলে `Constructor Function`। আমরা যেভাবেই তৈরি করি না কেন সবকিছুর পিছনে ঐ `Object` কনস্ট্রাক্টরই কাজ করছে। এই `Object` এর মধ্যে কিছু ্ প্রোপার্টিজ আছে যা আমরা দুনিয়ায় যতো অবজেক্টই বানাবো সবকিছুতে ইনহেরিট হয়ে যাবে। আমরা একটু সেসব প্রোপার্টিজ দেখার চেষ্টা করি। এর জন্য আমাদের একটু ব্রাউজারের কনসোলে যেতে হবে। নিচের স্ক্রিনশটটি একটু খেয়াল করুন আপনারা। + +![Object methods](./Screenshot_1.png) + +প্রথমে আছে কনস্ট্রাকটর। আমরা `Object` এর আগে `new` লাগিয়ে যে অবজেক্ট তৈরি করেছিলাম সেটাকে সেজন্য কন্সট্রাক্টর বলে। এরপর আছে `hasOwnProperty` এটা দিয়ে আমরা কোনো প্রোপার্টি ঐ অবজেক্টের নিজস্ব প্রোপার্টি কিনা তা চেক করতে পারবো। এছাড়াও `toString`, `valueOf`, `toLocaleString` ইত্যাদি প্রোপার্টিজ আছে। যেগুলো আমরা অবজেক্টে ডিফাইন না করলেও তারা প্রয়োজনে সেই মেথডগুলো ব্যবহার করতে পারবে। এগুলো মূলত আমরা যখন অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিং করতে যাবো তখন এসব দরকার পড়বে। এখন অবশ্য ES6 আসার পরে অতো ডিপলি অবজেক্ট অরিয়েন্টেড প্রোগ্রামিং করার দরকার পড়ে না। তারপরও যতটুকু দরকার আমরা শিখে নিবো।এই মুহূর্তে সেটা নিয়ে মাথা না ঘামালেও চলবে। আমরা যদি `Oboject` লিখে একটা ডট (.) দিই তাহলে অনেক প্রোপার্টিজ আসবে। এখন এদের মধ্যে কোন কোন প্রোপার্টিজ ইনহেরিট হবে বা এক্সটেন্ডেড হবে এবং কোন কোন প্রোপার্টিজ হবে না। উপরের ছবিটি খেয়াল করুন। প্রোটটাইপের মধ্যে যে সকল প্রোপার্টিজ আছে সেগুলো ইনহেরিট বা এক্সটেন্ডেড হবে। আর যেগুলো নেই সেগুলো হবে না। + +এই প্রোপার্টিজগুলোর মধ্যে আমরা একটু `freeze` প্রোপার্টিটা দেখি। ধরেন আমরা আমাদের microphone অবজেক্টে নতুন একটা প্রোপার্টি অ্যাড করতে চাইছি। তাহলে আমাদের নিচের কোডটা লিখতে হবে।] + +```js +const microphone = { + brand: 'Fifine', + indictor: true, + price: 8000, + color: 'Black', + startRecording() { + console.log('recording started'); + }, + stopRecording() { + console.log('recording stopped'); + }, +}; + +microphone.newProperty = 'New Property'; +console.log(microphone); +/* { + brand: 'Fifine', + indictor: true, + price: 8000, + color: 'Black', + startRecording: [Function: startRecording], + stopRecording: [Function: stopRecording], + newProperty: 'New Property' +} */ +``` + +কিন্তু অনেক সময় এমন অবজেক্ট নিয়ে আমরা কাজ করতে পারি যেখানে আমরা ডাটা এন্ট্রি রেস্ট্রিক্ট করে দিতে চাইছি। সোজা কথায় আমরা এখানে ডাটা ইনপুট দিতে দিবো না। সেই ক্ষেত্রে `freeze` মেথডটা অনেক কাজে আসে। + +```js +const microphone = { + brand: 'Fifine', + indictor: true, + price: 8000, + color: 'Black', + startRecording() { + console.log('recording started'); + }, + stopRecording() { + console.log('recording stopped'); + }, +}; + +Object.freeze(microphone); +microphone.newProperty = 'New Property'; +console.log(microphone); +/* { + brand: 'Fifine', + indictor: true, + price: 8000, + color: 'Black', + startRecording: [Function: startRecording], + stopRecording: [Function: stopRecording], +} */ +``` + +খেয়াল করুন এখানে আমাদের অবজেক্ট কিন্তু আপডেট হয়নি। এই মেথড ব্যবহার করে আমরা অবজেক্টকে লক করে দিতে পারি। আরো দুইটা মেথড দেখি আমরা। একটা `keys` এবং অন্যটা `values`। + +```js +console.log(Object.keys(microphone)); // ['brand', 'indictor', 'price', 'color', 'startRecording', 'stopRecording']; +console.log(Object.values(microphone)); + +/* +[ + 'Fifine', + true, + 8000, + 'Black', + [Function: startRecording], + [Function: stopRecording] +] +*/ +``` + +`Object.keys()` অবজেক্টের সব keys অ্যারে আকারে রিটার্ন করবে এবং `Object.values()` অবজেক্টের সব values অ্যারে আকারে রিটার্ন করবে। এখন এগুলো আমাদের কি দরকার? আমরা তো এগুলো ছাড়াও লুপ চালিয়ে কী এবং ভ্যালু বের করে আনতে পারি এভাবে- + +```js +for (let k in microphone) { + console.log(k, microphone[k]); +} + +/* +brand Fifine +indictor true +price 8000 +color Black +startRecording [Function: startRecording] +stopRecording [Function: stopRecording] +*/ +``` + +এখানে ভ্যালু বের করার জন্য যে অবজেক্ট নোটেশন ব্যবহার করা হয়েছে তাকে বলে অ্যারে নোটেশন। অবজেক্ট থেকে ভ্যালু দুইটা নোটেশন ইউজ করে বের করা যায়। + +- Dot notation (microphone.brand) +- Array notation (microphone['brand]) + +যখন আমরা ডায়নামিক্যালি কোনো কী নিবো তখন আমরা জানি না সেটা কিরকম। তাই আমরা এক্ষেত্রে সবসময় অ্যারে নোটেশন ইউজ করবো। এবার মূলকথায় ফিরে যায়। আমরা তো এভাবেও কী আর ভ্যালু পাচ্ছি। তাহলে ঐ দুইটা মেথডের কাজ কি? আমরা একটু দেখি। + +```js +const empty = {}; +console.log(empty); // {} +console.log(Boolean(empty)); // true +``` + +আমরা যদি জানতে চাই আমাদের অবজেক্টটা সত্যিই ফাঁকা কিনা তাহলে এভাবে পারবো না। কারণ ফাঁকা অবজেক্ট, ফাঁকা অ্যারে সবসময় true রিটার্ন করবে। সেক্ষেত্রে আমরা `Object.keys()` এর সাহায্য নিবো। + +```js +const empty = {}; +console.log(Object.keys(empty)); // [] +``` + +এখন ফাঁকা অ্যারেও তো true রিটার্ন করবে কারণ ফাঁকা অ্যারেও একটা truthy value. আমাদের অবজেক্ট প্রোপারলি ফাঁকা কিনা তা জানার জন্য আমাদেরকে নিচের কাজটা করতে হবে। + +```js +const empty = {}; +console.log(Object.keys(empty).length === 0); // true +``` + +তার মানে যদি লেংথ ০ হয় তাহলে আমাদের অবজেক্টটা ফাঁকা বলে ধরে নিতে পারি। + +এছাড়াও আছে `Object.entries()` মেথড। এটার কাজটা আমরা দেখি একটু। + +```js +console.log(Object.entries(microphone)); +/* +[ + [ 'brand', 'Fifine' ], + [ 'indictor', true ], + [ 'price', 8000 ], + [ 'color', 'Black' ], + [ 'startRecording', [Function: startRecording] ], + [ 'stopRecording', [Function: stopRecording] ] +] +*/ +``` + +ছিল অবজেক্ট। হয়ে গেলো কী আর ভ্যালু এর জন্য আলাদা আলাদা অ্যারে। এটা আমাদের ভবিষ্যতে অনেক কাজে লাগবে। + +এখন ধরেন আমাদের কাছে একটা অ্যারে আছে। আমরা চাইছি সেটা থেকে অবজেক্ট বানাতে। তা জন্য আমাদের ব্যবহার করতে হবে `fromEntries` মেথডটি। + +```js +const arr = [ + ['brand', 'Fifine'], + ['indictor', true], + ['price', 8000], + ['color', 'Black'], +]; + +console.log(Object.fromEntries(arr)); // { brand: 'Fifine', indictor: true, price: 8000, color: 'Black' } +``` + +### Function vs Method + +যখন একটা ফাংশন একটা অবজেক্টের মধ্যে থাকে তখন আমরা সেটাকে বলি মেথড। তাহলে আমরা যে array.filter(), array.push(), array.map(), array.splice() ব্যবহার করেছি এগুলো সবগুলোই হচ্ছে মেথড। এরা ফাংশন না। ফাংশন আর মেথডের মধ্যে একটাই পার্থক্য। ফাংশন স্বাধীনভাবে যেকোনো জায়গায় কল করা যায় কিন্তু মেথড পারা যায় না। একটা উদাহরণ দিলে আমরা ভালভাবে বুঝতে পারবো। + +```js +const microphone = { + brand: 'Fifine', + indictor: true, + price: 8000, + color: 'Black', + startRecording() { + console.log('recording started'); + }, + stopRecording() { + console.log('recording stopped'); + }, +}; + +function startRecording() { + console.log('recording started'); +} + +startRecording(); + +microphone.startRecording(); +``` + +এখানে `startRecording` ফাংশনটা অবজেক্টের ভিতরেও আছে, আবার বাইরেও আছে। এখন বাইরের ফাংশনকে চাইলে আমরা এমনিই কল করতে পারবো। কিছু ছাড়াই। কিন্তু অবজেক্টের ফাংশনকে যদি কল করতে চাই তাহলে অবশ্যই `microphone.startRecording()` লিখতে হবে। এটাই বেসিক পার্থক্য। তাহলে আমরা কোনোকিছুর পর ডট দিয়ে যাই লিখবো অর্থাৎ অবজেক্টের মধ্যে কোনো ফাংশন থাকলে সেগুলো সবগুলো হলো মেথড। আর ইন্ডিপেন্ডেন্টলি যা লিখবো সেগুলো হচ্ছে ফাংশন। + +## Object as a Data Structure + +আমরা চাইছি কয়েকজন ছাত্রের ইনফরমেশন স্টোর করতে। সেখানে থাকবে একজন ছাত্রের একটা ইউনিক আইডি, তার নাম এবং তার ইমেইল। এখন আমরা প্রথমে একটা ইউনিক আইডি জেনারেট করার ফাংশন তৈরি করে ফেলি। + +```js +function uuidv4() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + const r = (Math.random() * 16) | 0; + const v = c == 'x' ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); +} +``` + +এই ফাংশনটা গুগল থেকে নেয়া। এখন আমরা এই ছাত্রদের ইনফরমেশন অ্যারে দিয়েও স্টোর করতে পারি আবার অবজেক্ট দিয়েও পারি। প্রথমেই বলে রাখি সব কাজের জন্য অ্যারে ভাল না আবার সব কাজের জন্য অবজেক্টও ভাল না। আমাদেরকে আমাদের কাজের উপর ভিত্তি করে সিদ্ধান্ত নিতে হবে কখন আমরা অ্যারে ইউজ করবো আর কখন অবজেক্ট। প্রথমে আমরা একটু অ্যারে নিয়ে কাজ করে দেখি। এরপর অবজেক্ট নিয়ে করবো। + +### Array + +আমাদের সমস্ত ছাত্রের ইনফরমেশন আমরা অ্যারেতে স্টোর করে রাখি এখন। + +```js +const students = [ + { + id: uuidv4(), + name: 'Md Al-Amin', + email: 'alamin@test.com', + }, + { + id: uuidv4(), + name: 'Akib Ahmed', + email: 'akib@test.com', + }, + { + id: uuidv4(), + name: 'Elias Emon', + email: 'elias@test.com', + }, +]; +``` + +যেহেতু আমরা UI নিয়ে কাজ করছি না তাই আমরা চাইবো না বারবার আইডি চেইঞ্জ হোক। আমরা একবার প্রোগ্রাম রান করে যে আউটপুট জেনারেট হবে সেটাকেই স্টোর করে রাখবো। বারবার আইডি চেইঞ্জ হলে আমরা আমাদের যে অপারেশন তা ঠিকভাবে করতে পারবো না। সুতরাং আমরা প্রথমবার রান করার পর সেই আউটপুটকে স্টোর করে নিই আগে। + +```js +const students = [ + { + id: '67de71e5-0eac-474f-ab51-850ba9b31ed5', + name: 'Md Al-Amin', + email: 'alamin@test.com', + }, + { + id: 'ebdf6b78-c32b-4b1d-8574-e8c655b05c1e', + name: 'Akib Ahmed', + email: 'akib@test.com', + }, + { + id: 'ee729e84-a84e-4adf-b32c-4647a7114d5b', + name: 'Elias Emon', + email: 'elias@test.com', + }, +]; +``` + +অ্যারেতে স্টোর করে রাখলে আমরা কিছু সুবিধা পাবো। সেগুলো হলোঃ + +1. Create a new one +2. Update +3. Delete +4. Filter +5. Easily Traverse + +এবার আমরা এক এক করে এই কাজগুলো দেখি। + +- Create a new one + +এটা সবচেয়ে সহজ কাজ। আমরা জানি আমরা যখন অ্যারেতে একটা ডাটা ইনসার্ট করতে চাই দুইটা মেথড আমরা ইউজ করতে পারি। যদি সবার শেষে ইনসার্ট করতে চাই তাহলে `push` মেথড ব্যবহার করবো, আর যদি সবার প্রথমে ইনসার্ট করতে চাই তাহলে `unshift` মেথড ব্যবহার করবো। কিন্তু `unshift` অনেক এক্সপেন্সিভ। কেন এক্সপেন্সিভ? কারণ আমাকে প্রতিটা ইলেমেন্ট এক ঘর করে ডান পাশে শিফট করতে হচ্ছে। যার কারনে অনেক বেশি অপারেশন ঘটাতে হচ্ছে। অর্থাৎ এর কমপ্লেক্সিটি O(n)। অন্যদিকে `push` মেথডে আমার কাউকে সরাতে হচ্ছে না। শুধু শেষে ডাটাটা বসিয়ে দিলেই হলো। অর্থাৎ এর কমপ্লেক্সিটি O(1)। O(n) হলো ডাটা সাইজের উপর এর এক্সিকিউশন টাইম নির্ভর করে। সাইজ ছোট হলে কম সময় আর সাইজ বড় হলে বেশি সময়। এটার সমস্যা হলো আমরা এখানে বিগ অ্যামাউন্টের ডাটা স্টোর করে রাখতে পারবো না। আর O(1) হলো ডাটার সাইজ কতো বড় বা ছোট সেটা বিবেচ্য না। সেটা একটা নির্দিষ্ট সময়েই এক্সিকিউট হবে তা বড় সাইজের ডাটা হোক বা ছোট সাইজের। তার এক্সিকিউশন টাইম কন্সট্যান্ট। এক্ষেত্রে ডাটা ইনসার্টের জন্য আমরা `push` মেথড ব্যবহার করবো। + +```js +students.push({ + id: '0a2c956c-a9f4-48b9-83fa-551b432dfb2b', + name: 'Fahim Faisal', + email: 'fahim@test.com', +}); +``` + +এখন আমাদের প্রোগ্রাম রান করালে দেখা যাবে আমাদের অ্যারেতে নতুন ডাটা ক্রিয়েট হয়ে গেছে। + +- Update + +আমরা দুইভাবে আপডেট করতে পারি। একটা হচ্ছে যাকে আপডেট করতে হবে find মেথডের মাধ্যমে সেই অবজেক্টকে বের করে তাকে আপডেট করা। আরেকটা হলো ঐ অবজেক্টের ইনডেক্সকে findIndex মেথডের মাধ্যমে বের করে সেটা ধরে আপডেট করা। অবজেক্ট ধরে যদি আপডেট করতে চাই সেক্ষেত্রে একটা সমস্যা আছে। সেটা একটু আমরা দেখি। + +```js +const idToUpdate = 'ee729e84-a84e-4adf-b32c-4647a7114d5b'; +const updatedData = { + name: 'Habiba Akhtar', + email: 'habiba@test.com', +}; + +let updatedObj = students.find((item) => item.id === idToUpdate); +updatedObj = { + id: idToUpdate, + ...updatedObj, +}; +console.log('Updated', students); +/* +Updated [ + { + id: '67de71e5-0eac-474f-ab51-850ba9b31ed5', + name: 'Md Al-Amin', + email: 'alamin@test.com' + }, + { + id: 'ebdf6b78-c32b-4b1d-8574-e8c655b05c1e', + name: 'Akib Ahmed', + email: 'akib@test.com' + }, + { + id: 'ee729e84-a84e-4adf-b32c-4647a7114d5b', + name: 'Elias Emon', + email: 'elias@test.com' + }, + { + id: '0a2c956c-a9f4-48b9-83fa-551b432dfb2b', + name: 'Fahim Faisal', + email: 'fahim@test.com' + } +] +*/ +``` + +কিছুই আপডেট হলো না। কারণ আমরা অবজেক্ট অ্যাসাইন করছি। আর যেহেতু অ্যাসাইন করছি সেহেতু এর রেফারেন্সও আলাদা হয়ে গেছে। আলাদা রেফারেন্সের কারণে আমার আপডেট কাজ করছে না। এবার আসি ইনডেক্স বের করে কিভাবে আপডেট করতে পারি সেটা নিয়ে। + +```js +const idToUpdate = 'ee729e84-a84e-4adf-b32c-4647a7114d5b'; +const updatedData = { + name: 'Habiba Akhtar', + email: 'habiba@test.com', +}; + +const updatedIndex = students.findIndex((item) => item.id === idToUpdate); +students[updatedIndex] = { + ...students[updatedIndex], + ...updatedData, +}; +console.log('Updated', students); +``` + +তিনটা ডট দেয়াকে জাভাস্ক্রিপ্টে বলে স্প্রেড অপারেটর। এর মানে হলো অরিজিনাল অবজেক্টে যা যা আছে সবই থাকবে। আর নতুন ডাটা অনুযায়ী সেটা আপডেট হবে। যখন কোনো কিছু রিঅ্যাসাইনের কাজ আসবে তখন আমরা find ব্যবহার না করে findIndex ব্যবহার করবো। এই আপডেট করা মোটামুটি রকমের কমপ্লেক্স। তাই এর কমপ্লেক্সিটি আমরা O(n) হিসেবে ধরতে পারি। + +- Delete + +ডিলিট করাটা তুলনামূলক সহজ। কিন্তু আমরা ডিলিটের জন্য দুইটা মেথড ইউজ করতে পারি `splice` এবং `filter`। এই দুইটা মেথডের কমপ্লেক্সিটি O(n)। এখানে আমরা splice নিয়ে কাজ করছি। পরের ধাপে আমরা filter অপারেশন দেখাবো। আমরা যদি আমাদের upodatedIndex ডিলিট করতে চাই তাহলে এভাবে লিখতে হবে। + +```js +students.splice(updatedIndex, 1); +``` + +- Filter + +```js +const filteredStudents = students.filter((item) => item.id !== idToUpdate); +console.log(filteredStudents); +``` + +- Easily Traverse + +অ্যারের ক্ষেত্রে ট্রাভার্স করা অনেক সহজ। ধরি আমরা ছাত্রদের নাম জানতে চাইছি। তিনভাবে আমরা অ্যারে ট্রাভার্সের মাধ্যমে নাম বের করে আনতে পারি। এগুলো হলো। `for` loop, `for in` loop, `for of` loop। নিচে তিনটারই উদাহরণ দেয়া হলো। + +```js +for (let i = 0; i < students.length; i++) { + console.log(students[i].name); +} + +for (let i in students) { + console.log(students[i].name); +} + +for (let student of students) { + console.log(student.name); +} +``` + +এছাড়াই কিছু বিল্ট-ইন মেথড রয়েছে অ্যারে ট্রাভার্সের জন্য। যেমন `forEach`, `map`, `filter`, `every`, `reduce`, `some`, `find`, `findIndex` ইত্যাদি। তাহলে আমরা বুঝলাম যে অ্যারে অনেক সহজে ট্রাভার্স করা যায়। এটার কমপ্লেক্সিটি O(n)। + +### Object Over Array + +এবার আমরা আমাদের ছাত্রদের অ্যারেকে একটা অবজেক্টে রূপান্তরিত করি এবং একে একে অ্যারের ক্ষেত্রে যে যে অপারেশন করেছি সেই সেই অপারেশন করার চেষ্টা করি। + +```js +const students = { + '67de71e5-0eac-474f-ab51-850ba9b31ed5': { + id: '67de71e5-0eac-474f-ab51-850ba9b31ed5', + name: 'Md Al-Amin', + email: 'alamin@test.com', + }, + 'ebdf6b78-c32b-4b1d-8574-e8c655b05c1e': { + id: 'ebdf6b78-c32b-4b1d-8574-e8c655b05c1e', + name: 'Akib Ahmed', + email: 'akib@test.com', + }, + 'ee729e84-a84e-4adf-b32c-4647a7114d5b': { + id: 'ee729e84-a84e-4adf-b32c-4647a7114d5b', + name: 'Elias Emon', + email: 'elias@test.com', + }, +}; +``` + +আমাদের অবজেক্ট রেডি। এবার আমরা অপারেশনগুলো দেখি এক এক করে। + +- Create a new one + +অ্যারেতে আমরা সহজেই push মেথড ইউজ করে ডাটা ইনসার্ট করেছিলাম। কিন্তু অবজেক্টে তো এরকম কিছু নেই। তাহলে আমরা কিভাবে এই অপারেশন চালাবো। দেখি একটু কিভাবে করা যায়। + +```js +const std = { + id: uuidv4(), + name: 'Feroz Khan', + email: 'feroz@test.com', +}; + +students[std.id] = std; +``` + +একটাই উপায়। এবং সবচেয়ে সহজ উপায়। এই উপায়ে আপনি যতো চান ততো ডাটা ক্রিয়েট করতে পারবেন। খুব সহজ। আর এর কমপ্লেক্সিটি হলো O(1)। + +- Update + +যেহেতু এটা অ্যারে না সেহেতু এখানে find বা findIndex কিছুই কাজ করবে না। তাহলে কিভাবে আপডেট করবো। খুব সহজ। চলুন দেখা যাক। + +```js +const idToBeUpdated = 'ee729e84-a84e-4adf-b32c-4647a7114d5b'; +const updatedData = { + name: 'HM Azizul', + email: 'azizul@test.com', +}; +students[idToBeUpdated] = { + ...students[idToBeUpdated], + ...updatedData, +}; +``` + +এখন যদি আপনি প্রোগ্রাম রান করান দেখবেন আপনার ডাটা আপডেট হয়ে গেছে। কিন্তু যেহেতু এখানে কোনো ধরণের বিল্ট-ইন মেথড লাগেনি তাই এর কমপ্লেক্সিটি হবে O(1)। + +- Delete + +অবজেক্ট থেকে ডিলিট করা খুব সহজ।মানে এত সহজ হওয়া সম্ভব না। জাস্ট একটা কীওয়ার্ড ব্যবহার করলে ডিলিট হয়ে যাবে। + +```js +delete students[idToBeUpdated]; +``` + +কাজ শেষ। কমপ্লেক্সিটি O(1)। + +- Get anything if you have the key + +যদি আমাদের কোনো অবজেক্টের কী জানা থাকে তাহলে ১ সেকেন্ডের মধ্যে আমরা সেই অবজেক্টকে পেয়ে যাবো। কিভাবে> দেখুন তাহলে- + +```js +console.log(students['67de71e5-0eac-474f-ab51-850ba9b31ed5']); +``` + +জাস্ট এটুকুই। আর এটার কমপ্লেক্সিটিও O(1)। + +- Traverse + +আমরা for in লুপ ব্যবহার করে খুব সহজেই অবজেক্ট ট্রাভার্স করতে পারি। যেমন যদি আমরা অবজেক্টে থাকা সবার নাম বের করে আনতে চাই তাহলে কিভাবে করবো? + +```js +for (let key in students) { + console.log(students[key].name); +} +``` + +কিন্তু এটা একটা ইম্পেরেটিভ ওয়ে। আমরা যখন রিয়্যাক্ট নিয়ে কাজ করবো তখন jsx এ কিন্তু for in ব্যবহার করতে পারবো না। আমাদের দরকার একটা ডিক্লারেটিভ ওয়ে। সেটার জন্য আমরা অবজেক্টের আলোচনায় দুইটা মেথডের কথা বলেছিলাম। একটা ছিল `Object.keys()` এবং অন্যটা হলো `Object.values()`। চলুন দেখি এগুলো কিভাবে অ্যাপ্লাই করতে পারি। + +```js +Object.keys(students).forEach((key) => { + const student = students[key]; + console.log(student.name); +}); +``` + +এখানে key না নিয়েও আমরা সরাসরি value নিয়ে কাজ করতে পারতাম। যেমন + +```js +Object.values(students).forEach((student) => { + console.log(student.name); +}); +``` + +এটার মাধ্যমে আমরা অবজেক্ট থেকে অ্যারে বানিয়ে অ্যারের সমস্ত কাজ আমরা করতে পারি। এতে কিন্তু আমাদের কোনো এক্সট্রা মেমোরি লাগছে না। কারণ আমরা এটাকে কোথাও স্টোর করে রাখছি না। এটা তার কাজ শেষ করে গার্বেজ কালেক্ট করে ক্লিয়ার করে ফেলবে। + +তাহলে দেখা যাচ্ছে যে যে কাজ আমরা অ্যারে দিয়ে করতে পারতাম সেগুলো আমরা অবজেক্ট দিয়েও করতে পারি। এবং অনেক ক্ষেত্রে অনেক সহজেই করতে পারি। + +## Comparison of object and array operation costs + +```js +const arr = []; +const arrToObj = {}; +for (let i = 0; i < 5000000; i++) { + const o = { + id: i, + value: i, + }; + arr.push(o); + arrToObj[i] = o; +} + +console.time('array'); +let id = 4999999; +const obj = arr.find((item) => item.id === id); +obj.value = 555; +console.timeEnd('array'); // 104.901ms + +console.time('obj'); +arrToObj[id].value = 999; +console.timeEnd('obj'); // 0.019ms +``` + +অ্যারের অপারেশনে সময় লেগেছে ১০৪.৯০১ মিলিসেকেন্ড আর অবজেক্টের অপারেশনে লেগেছে ০.০১৯ মিলিসেকেন্ড। + +```js +console.time('array'); +arr.unshift({ + id: 5000000, + value: 5000000, +}); +console.timeEnd('array'); // 15.084ms + +console.time('obj'); +arrToObj[5000000] = { + id: 5000000, + value: 5000000, +}; +console.timeEnd('obj'); // 0.018ms +``` + +অ্যারের জন্য লেগেছে ১৫.০৮৪ মিলিসেকেন্ড আর অবজেক্টের ক্ষেত্রে লেগেছে ০.০১৮ মিলিসেকেন্ড। + +```js +console.time('array'); +const index = arr.findIndex((item) => item.id === 4000000); +arr.splice(index, 1); +console.timeEnd('array'); // 93.135ms + +console.time('obj'); +delete arrToObj[4000000]; +console.timeEnd('obj'); // 0.015ms +``` + +অ্যারের ক্ষেত্রে লেগেছে ৯৩.১৩৫ মিলিসেকেন্ড আর অবজেক্টের ক্ষেত্রে লেগেছে ০.০১৫ মিলিসেকেন্ড। + +সবক্ষেত্রে দেখা যাচ্ছে তাহলে অবজেক্ট বিজয়ী। তবে কিছু কিছু ক্ষেত্রে অ্যারে নিয়ে কাজ করা লাগে। যেমন যখন আমার অর্ডারড ডাটা লাগবে, অর্থাৎ সিকোয়েন্স মেইনটেইন করতে হবে তখন অ্যারে মাস্ট। + + + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/class-overview/Lecture-05-06/Screenshot_1.png b/docs/Lectures/Fundamentals/05/Screenshot_1.png similarity index 100% rename from class-overview/Lecture-05-06/Screenshot_1.png rename to docs/Lectures/Fundamentals/05/Screenshot_1.png diff --git a/docs/Lectures/Fundamentals/05/Source Code/Array Traverse/SourceCode.md b/docs/Lectures/Fundamentals/05/Source Code/Array Traverse/SourceCode.md new file mode 100644 index 0000000..7c12ba2 --- /dev/null +++ b/docs/Lectures/Fundamentals/05/Source Code/Array Traverse/SourceCode.md @@ -0,0 +1,72 @@ +
+ declarativeWay.js +

This is Source Code Of object.js

+ +```javascript +const numbers = [1, 2, 3, 4, 5]; + +// sum of array elements +sum = 0; +numbers.forEach((element) => { + sum += element; +}); +console.log(sum); + + +const cb = () => { + console.log('Hello') +} +numbers.forEach(cb) +// Hello +// Hello +// Hello +// Hello +// Hello + + +numbers.forEach(function (value, index, arr) { + // console.log(arguments) + console.log(value, index, arr) +}) +// [Arguments] { '0': 1, '1': 0, '2': [ 1, 2, 3, 4, 5 ] } +// [Arguments] { '0': 2, '1': 1, '2': [ 1, 2, 3, 4, 5 ] } +// [Arguments] { '0': 3, '1': 2, '2': [ 1, 2, 3, 4, 5 ] } +// [Arguments] { '0': 4, '1': 3, '2': [ 1, 2, 3, 4, 5 ] } +// [Arguments] { '0': 5, '1': 4, '2': [ 1, 2, 3, 4, 5 ] } + + +numbers.forEach(function (value, _, __) { + if(value % 2 === 0){ + console.log(value) + } +}) + +sum = 0; +numbers.forEach(function (v) { + sum += v +}) + +console.log(sum) + + +``` + +
+ + +
+ imperativeWay.js +

This is Source Code Of object.js

+ +```javascript + +const numbers = [1, 2, 3, 4, 5]; + +let sum = 0; +for (let i = 0; i < numbers.length; i++) { + sum += numbers[i]; +} + +``` + +
\ No newline at end of file diff --git a/src/lecture-05/Array Traverse/declarativeWay.js b/docs/Lectures/Fundamentals/05/Source Code/Array Traverse/declarativeWay.js similarity index 100% rename from src/lecture-05/Array Traverse/declarativeWay.js rename to docs/Lectures/Fundamentals/05/Source Code/Array Traverse/declarativeWay.js diff --git a/src/lecture-05/Array Traverse/imperativeWay.js b/docs/Lectures/Fundamentals/05/Source Code/Array Traverse/imperativeWay.js similarity index 100% rename from src/lecture-05/Array Traverse/imperativeWay.js rename to docs/Lectures/Fundamentals/05/Source Code/Array Traverse/imperativeWay.js diff --git a/docs/Lectures/Fundamentals/05/Source Code/SourceCode.md b/docs/Lectures/Fundamentals/05/Source Code/SourceCode.md new file mode 100644 index 0000000..a5a969e --- /dev/null +++ b/docs/Lectures/Fundamentals/05/Source Code/SourceCode.md @@ -0,0 +1,292 @@ +
+ arr.js +

This is Source Code Of arr.js

+ + +```javascript +const arr = [1, 2, 3, null, false, 4, 5, "", "test", 6, 7]; + +let count = 0; +for (let i = 0; i < arr.length; i++) { + for (let j = i; j < arr.length - 1; j++) { + if (!arr[j] || typeof arr[j] !== "number") { + arr[j] = arr[j + 1]; + arr[j + 1] = undefined; + } + } + if (arr[i] === undefined) { + count++; + } +} +arr.length -= count; +console.log(arr); + +// explanation +// arr = [1, false, true, '', 2, 3] +// i = 1, j = 3 +// 1, true, '', 2, 3, undefined +// i = true, j = 3 +// 1, '', 2, 3, undefined, undefined +// i = '', j = 3 +// 1, 2, 3, undefined, undefined, undefined + +// [1, 2, 3] + + +// shortcut +const filteredArray = arr.filter((v) => typeof v === 'number') + +console.log(filteredArray); + + +// using new array +const newArr = [] +for(let i = 0; i < arr.length; i++){ + if(typeof arr[i] === 'number'){ + newArr.push(arr[i]) + } +} +console.log(newArr); + + +// calculate fibonnacci number +function fib(n){ + if(n == 0 || n == 1) return n; + return fib(n - 1) + fib(n - 2) +} + +console.log(fib(10)); +``` + +
+ + + + + +
+ delete.js +

This is Source Code Of delete.js

+ + +```javascript + + +const arr = [ + { + id: 1, + value: 10, + }, + { + id: 2, + value: 20, + }, + { + id: 3, + value: 30, + }, + { + id: 4, + value: 40, + }, + { + id: 5, + value: 50, + }, +]; + + +// splice ==> mutable +// const index = arr.findIndex(item => { +// item.id === 4 +// }) +// const arr1 = arr.splice(index, 1) +// console.log(arr); + + +// filter ==> immutable +const arr2 = arr.filter(item => { + return item.id !== 4 +}) +console.log(arr); +console.log(arr2); + +``` + +
+ + + + +
+ object.js +

This is Source Code Of object.js

+ +```javascript + +// object literal +const microphone = { + brand: 'Fantech', + indicator: true, + price: 3400, + color: 'white', + + // methods + startRecording() { + console.log('Recording started'); + }, + stopRecording() { + console.log('Recording stopped'); + } +} + +/** + * There are two different parts in object + * 1. Noun / Adjective (State/data/property/field) + * 2. Verb / (functionalities -> start, stop) + */ + +microphone.startRecording() +microphone.stopRecording() +console.log(microphone); +console.log(Object); + + +// constructor function +const testObj = new Object() +testObj.name = 'Test Object' +testObj.time = new Date() +console.log(testObj); +console.log(testObj.time.getDate()); + +// object k freeze kore dey +// new property add korte dey na +Object.freeze(microphone) +microphone.newProperty = 'hi' +console.log(microphone); + + + +// get key and value +console.log(Object.keys(microphone)); +console.log(Object.values(microphone)); +// [ +// 'brand', +// 'indicator', +// 'price', +// 'color', +// 'startRecording', +// 'stopRecording' +// ] +// [ +// 'Fantech', +// true, +// 3400, +// 'white', +// [Function: startRecording], +// [Function: stopRecording] +// ] + + +// concat function +console.log('micro'.concat('phone')); +console.log('micro' + 'phone'); + + + +// notation +// dot notation -> microphone.brand +// array notation -> microphone[k] +for(let k in microphone){ + console.log(k, microphone[k]); +} +// brand Fantech +// indicator true +// price 3400 +// color white +// startRecording [Function: startRecording] +// stopRecording [Function: stopRecording] + + + +// check is a object is empty or not +const empty ={} +if(Object.keys(empty).length === 0){ + console.log('This object is empty'); +} + + +// object to key value pair +console.log(Object.entries(microphone)); +const array = [ + [ 'brand', 'Fantech' ], + [ 'indicator', true ], + [ 'price', 3400 ], + [ 'color', 'white' ] +] +console.log(Object.fromEntries(array)); +// { brand: 'Fantech', indicator: true, price: 3400, color: 'white' } + +``` + +
+ + + +
+ update.js +

This is Source Code Of update.js

+ +```javascript +const arr = [ + { + id: 1, + value: 10, + }, + { + id: 2, + value: 20, + }, + { + id: 3, + value: 30, + }, + { + id: 4, + value: 40, + }, + { + id: 5, + value: 50, + }, +]; + +// arr.findIndex ==> not mutable +// const index = arr.findIndex(v => { +// return v.id === 4; +// }) + +// console.log(index); +// console.log(arr); +// arr[index].value = 100 +// console.log(arr); + + + +// arr.find ==> mutable +const obj = arr.find((v) => { + return v.id === 4; +}); +obj.value = 100; +console.log(obj); +console.log(arr); + + + +const obj2 = arr[2] +obj2.value = 300 +console.log(obj); +console.log(arr); +``` + +
diff --git a/src/lecture-05/arr.js b/docs/Lectures/Fundamentals/05/Source Code/arr.js similarity index 100% rename from src/lecture-05/arr.js rename to docs/Lectures/Fundamentals/05/Source Code/arr.js diff --git a/src/lecture-05/delete.js b/docs/Lectures/Fundamentals/05/Source Code/delete.js similarity index 100% rename from src/lecture-05/delete.js rename to docs/Lectures/Fundamentals/05/Source Code/delete.js diff --git a/src/lecture-05/object.js b/docs/Lectures/Fundamentals/05/Source Code/object.js similarity index 100% rename from src/lecture-05/object.js rename to docs/Lectures/Fundamentals/05/Source Code/object.js diff --git a/src/lecture-05/task.md b/docs/Lectures/Fundamentals/05/Source Code/task.md similarity index 100% rename from src/lecture-05/task.md rename to docs/Lectures/Fundamentals/05/Source Code/task.md diff --git a/src/lecture-05/update.js b/docs/Lectures/Fundamentals/05/Source Code/update.js similarity index 100% rename from src/lecture-05/update.js rename to docs/Lectures/Fundamentals/05/Source Code/update.js diff --git a/resources/lecture-05/img/1.jpeg b/docs/Lectures/Fundamentals/05/img/1.jpeg similarity index 100% rename from resources/lecture-05/img/1.jpeg rename to docs/Lectures/Fundamentals/05/img/1.jpeg diff --git a/resources/lecture-05/img/10.jpeg b/docs/Lectures/Fundamentals/05/img/10.jpeg similarity index 100% rename from resources/lecture-05/img/10.jpeg rename to docs/Lectures/Fundamentals/05/img/10.jpeg diff --git a/resources/lecture-05/img/11.jpeg b/docs/Lectures/Fundamentals/05/img/11.jpeg similarity index 100% rename from resources/lecture-05/img/11.jpeg rename to docs/Lectures/Fundamentals/05/img/11.jpeg diff --git a/resources/lecture-05/img/2.jpeg b/docs/Lectures/Fundamentals/05/img/2.jpeg similarity index 100% rename from resources/lecture-05/img/2.jpeg rename to docs/Lectures/Fundamentals/05/img/2.jpeg diff --git a/resources/lecture-05/img/3.jpeg b/docs/Lectures/Fundamentals/05/img/3.jpeg similarity index 100% rename from resources/lecture-05/img/3.jpeg rename to docs/Lectures/Fundamentals/05/img/3.jpeg diff --git a/resources/lecture-05/img/4.jpeg b/docs/Lectures/Fundamentals/05/img/4.jpeg similarity index 100% rename from resources/lecture-05/img/4.jpeg rename to docs/Lectures/Fundamentals/05/img/4.jpeg diff --git a/resources/lecture-05/img/5.jpeg b/docs/Lectures/Fundamentals/05/img/5.jpeg similarity index 100% rename from resources/lecture-05/img/5.jpeg rename to docs/Lectures/Fundamentals/05/img/5.jpeg diff --git a/resources/lecture-05/img/6.jpeg b/docs/Lectures/Fundamentals/05/img/6.jpeg similarity index 100% rename from resources/lecture-05/img/6.jpeg rename to docs/Lectures/Fundamentals/05/img/6.jpeg diff --git a/resources/lecture-05/img/7.jpeg b/docs/Lectures/Fundamentals/05/img/7.jpeg similarity index 100% rename from resources/lecture-05/img/7.jpeg rename to docs/Lectures/Fundamentals/05/img/7.jpeg diff --git a/resources/lecture-05/img/8.jpeg b/docs/Lectures/Fundamentals/05/img/8.jpeg similarity index 100% rename from resources/lecture-05/img/8.jpeg rename to docs/Lectures/Fundamentals/05/img/8.jpeg diff --git a/resources/lecture-05/img/9.jpeg b/docs/Lectures/Fundamentals/05/img/9.jpeg similarity index 100% rename from resources/lecture-05/img/9.jpeg rename to docs/Lectures/Fundamentals/05/img/9.jpeg diff --git a/docs/Lectures/Fundamentals/05/resource.md b/docs/Lectures/Fundamentals/05/resource.md new file mode 100644 index 0000000..0214dbc --- /dev/null +++ b/docs/Lectures/Fundamentals/05/resource.md @@ -0,0 +1,397 @@ +# Resource + +# Lecture 5 - Array Operations - Imperative vs Declarative + +## Today’s Agenda + +- Array Operations + + - Imperative traverse + + ```jsx + const numbers = [2, 5, 6, 7, 89, 100]; + + let sum = 0; + for (let i = 0; i < numbers.length; i++) { + // console.log(numbers[i] * 2); + sum += numbers[i]; + } + console.log(sum); + ``` + + `Result:` + + ![code](./img/1.jpeg 'Result') + +
+ + > নিচের Array থেকে শুধু Number গুলা বের করতে হবে, কোন নতুন Array + > তৈরি করা যাবে না। এই সিস্টেমে করলে memory তে জায়গা কম লাগবে। + + ```jsx + let arr2 = [1, 2, 3, null, false, 6]; + + count = 0; + for (let i = 0; i < arr2.length; i++) { + for (let j = i; j < arr2.length - 1; j++) { + if (!arr2[j] || typeof arr2[j] !== 'number') { + arr2[j] = arr2[j + 1]; + arr2[j + 1] = undefined; + } + } + + if (arr2[i] == undefined) { + count++; + } + } + arr2.length -= count; //Array থেকে undefined গুলো বাদ দিলাম + console.log(count, arr2); + ``` + + `Result:` + + ![code](./img/2.jpeg 'Result') + +
+ + > ### Loop এর ভিতরের ঘটনা + + > #### i=0 তে, + + যখন j=3 হবে, + [1,2,3,false,undefined,6] + + যখন j=4 হবে, + [1,2,3,false,6,undefined] + + new array [1,2,3,false,6,undefined] + + > #### আবার + > + > i=1 তে, + + যখন j=3 হবে, + [1,2,3,6,undefined,undefined] + + যখন j=4 হবে, + [1,2,3,6,undefined,undefined] + + new array [1,2,3,6,undefined,undefined] + + > #### আবার + > + > i=2 তে, + + যখন j=4 হবে, + [1,2,3,6,undefined,undefined] + + new array [1,2,3,6,undefined,undefined] + + > #### আবার + > + > i=3 তে, + + যখন j=4 হবে, + [1,2,3,6,undefined,undefined] + + new array [1,2,3,6,undefined,undefined] + + > #### আবার + > + > i=4 তে + > যখন j=4 হবে, + > [1,2,3,6,undefined,undefined] + + new array [1,2,3,6,undefined,undefined] + + > #### আবার + > + > i=5 তে, + + j তে প্রবেশ করবে না, কারণ j=5 হওয়া সম্ভব না। + + > Final Array + > + > > [1,2,3,6,undefined,undefined] + > + > এই Array থেকে count বাদ দিলে আমাদের Final Result পাওয়া যাবে। + > + > > [1,2,3,6] + +

+ + > নতুন Array তৈরি করে উপরের কাজটা করা হল,এতে বেশি Memory লাগবে + + ```jsx + const arr = [1, 2, 3, null, false, 4, 5, '', 'test', 6, 7]; + + /* const filteredArray = arr.filter((val) => typeof val === 'number'); + console.log(filteredArray); */ + + const newArr = []; + for (let i = 0; i < arr.length; i++) { + if (typeof arr[i] === 'number') { + newArr.push(arr[i]); + } + } + console.log(newArr); + ``` + + `Result:` + + ![code](./img/3.jpeg 'Result') + +

+ + > ### Declerative way তে Travers করা [Built in method] + + ```jsx + let arr20 = [1, 2, 3, 4]; + arr20.forEach((value, index, arr) => { + console.log(`index:${index} value:${value} Array:${arr}`); + }); + ``` + + `Result:` + + ![code](./img/4.jpeg 'Result') + + forEach() এ argument আকারে যে function pass করা হয়, তার + Parameter এ ৩ টি item pass করা যায়। + + প্রথমটাঃValue + + দ্বিতীয়টাঃindex + + তৃতীয়টাঃওই Array টা + + আমি যদি চাই value আর index বাদ দিয়ে শুধু Array টা show করাতে + তখন value ও index এর জায়গায় Underscore( _ ) ব্যবহার করতে + হবে। তবে arrow function এ পর পর ২ টা Underscore( _ ) ব্যবহার + করলে এটা error throw korbe + + > Underscore এর মাধ্যমে value ও index Skip করিছি + + ```jsx + arr20.forEach(function (_, _, arra) { + console.log(`Array:[${arra}]`); + }); + ``` + + `Result:` + + ![code](./img/5.jpeg 'Result') + + **_Error Show_** করবে কারণ forEach() এর Argument এ Arrow Function + ব্যবহার করেছি। + + ```jsx + let arr404 = [1, 2, 3]; + arr404.forEach((_, _, arra) => { + console.log( + `Array:[${arra}]` + ); /*SyntaxError: Duplicate parameter name not allowed in this context*/ + }); + ``` + +

+ + - Update + + ```jsx + const arr = [ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 4, value: 40 }, + { id: 5, value: 50 }, + ]; + + const obj2 = arr[2]; + obj2.value = 300; + console.log(obj2); + + /* const index = arr.findIndex(function (val) { + return val.id === 4; + }); + + arr[index].value = 400; + + console.log(arr); */ + ``` + + `Result:` + + ![code](./img/6.jpeg 'obj2 Result') + +
+ + ```jsx + /*find() Method Mutable Way তে কাজ করে, কারণ এটা মুল Array কে + change করে ফেলে।*/ + const obj = arr.find(function (val) { + return val.id === 4; + }); + + obj.value = 400; + console.log(obj); + ``` + + `Result:` + + ![code](./img/7.jpeg 'obj Result') + +
+ + > arr ও পরিবর্তন হয়ে গেছে + + ```jsx + console.log(arr); + ``` + + `Result:` + + ![code](./img/8.jpeg 'arr Result') + +

+ + ```jsx + console.log(arr[3] === obj); + + const a = { a: 10 }; + const b = { a: 10 }; + const c = a; + console.log(a === c); + ``` + +
+ + - Delete + + ```jsx + const arr5 = [ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 4, value: 40 }, + { id: 5, value: 50 }, + ]; + + // splice -> mutable + const index = arr5.findIndex((item) => item.id === 4); + const arr1 = arr5.splice(index, 1); //একটা item Delete হয়ে যাবে + console.log(arr5); + ``` + + `Result:` + + ![code](./img/9.jpeg 'Updated arr5 Result') + +

+ + ```jsx + // filter -> immutable + const arr8 = [ + { id: 1, value: 100 }, + { id: 2, value: 200 }, + { id: 3, value: 300 }, + { id: 4, value: 400 }, + { id: 5, value: 500 }, + ]; + const arr7 = arr8.filter((item) => item.id !== 4); + console.log(arr7); + ``` + + `arr7 Result:` + + ![code](./img/10.jpeg 'arr7 Result') + + ```jsx + console.log(arr8); + ``` + + `arr8 বা মুল Array এর কোন পরিবর্তন হয়নিঃ` + + ![code](./img/11.jpeg 'arr8') + +

+ + - Mutation + - Map + - Filter + - Reduce + - Deep copy vs Shallow copy + +- Object Deep Dive + + - Object Operations + + ```jsx + // Object Literal + const microphone = { + brand: 'Fifine', + indictor: true, + price: 8000, + color: 'Black', + startRecording() { + console.log('recording started'); + }, + stopRecording() { + console.log('recording stopped'); + }, + }; + /* Object.freeze(microphone); + microphone.newProperty = 'my new property'; + console.log(microphone); */ + + // console.log(Object.keys(microphone)); + // console.log(Object.values(microphone)); + + /** + * There are two different parts in object + * 1. Noun / Adjective (State/data/property/field) + * 2. Verb (functionalities -> start, stop) + */ + + // Constructor function + // const testObj = new Object(); + // testObj.name = 'Test Object'; + // testObj.time = new Date(); + // console.log(testObj); + + /* for (let k in microphone) { + console.log(k, microphone[k]); + } */ + + // dot notation microphone.brand + // array notation microphone['brand'] + + const empty = {}; + console.log(Object.keys(empty).length === 0); + console.log(Object.entries(microphone)); + const arr = [ + ['brand', 'Fifine'], + ['indictor', true], + ['price', 8000], + ['color', 'Black'], + ]; + + console.log(Object.fromEntries(arr)); + ``` + + - Function vs Method + - Brief discussion on Prototype + +- Object as a Data Structure + - Array Operations + - Object Over Array +- Multi Dimensional Array + +### Important Links: + +- [Make Fun Of Javascript Array](https://www.youtube.com/playlist?list=PL_XxuZqN0xVDr08QgQHljCecWtA4jBLnS) +- [Class Overview](../../class-overview/Lecture-05-06/README.md) + +### Task: + +- How can we use object as a data structure? যে কাজগুলো আমরা array ব্যবহার করে করতে পারি সেগুলো কিভাবে object দিয়ে করতে পারি? diff --git a/docs/Lectures/Fundamentals/06/Overview.md b/docs/Lectures/Fundamentals/06/Overview.md new file mode 100644 index 0000000..9a99d4a --- /dev/null +++ b/docs/Lectures/Fundamentals/06/Overview.md @@ -0,0 +1,1423 @@ +## JavaScript Array and Object Deep Dive + +এই দুই লেকচারে আমরা আজ অ্যারে এবং অবজেক্ট নিয়ে বিশদ আলোচনা করবো। যেহেতু এই দুই লেকচার একটার সাথে একটা রিলেটেড তাই আমার কাছে দুইটা লেকচারের ওভারভিউ একসাথে লেখাটা যুক্তিযুক্ত বলে মনে হয়েছে। আমাদের আজকের এজেন্ডা হলো- +## Array Operations + +### Imperative Traverse + +আমাদেরকে যদি বলা হয় একটা অ্যারে ট্রাভার্স করার জন্য, আমরা খুব সহজেই একটা লুপ চালিয়ে ট্রাভার্স করে ফেলতে পারি। এখন প্রশ্ন আসতে পারে ট্রাভার্স কি। ট্রাভার্স হলো ধরুন আমরা একটা অ্যারের প্রতিটা ইলেমেন্ট যেমন লুপ চালিয়ে টাচ করে যে অপারেশন করা দরকার করতে পারি এটাকেই বলে ট্রাভার্স। যেমনঃ + +```js +const numbers = [2, 5, 6, 7, 89, 100]; + +for (let i = 0; i < numbers.length; i++) { + console.log(numbers[i]); +} +``` + +আমরা সব ইলেমেন্ট প্রিন্ট করে ফেলতে পারি এভাবে `numbers` অ্যারের। আমরা যদি চাই প্রতিটা ইলেমেন্ট ২ দ্বারা গুণ করে সেই আউটপুট দেখাবো সেটাও পারবো। + +```js +const numbers = [2, 5, 6, 7, 89, 100]; + +for (let i = 0; i < numbers.length; i++) { + console.log(numbers[i] * 2); +} +``` + +এবার যদি আমরা চাই সব ইলেমেন্টের যোগফল বের করবো তাও পারবো। + +```js +const numbers = [2, 5, 6, 7, 89, 100]; + +let sum = 0; +for (let i = 0; i < numbers.length; i++) { + sum += numbers[i]; +} + +console.log(sum); +``` + +একে বলে Imperative Traversing। কারণ আমরা কোথা থেকে লুপ শুরু হবে তা বলে দিয়েছি, কোথায় গিয়ে থামবে তাও বলে দিয়েছি, এমনকি কিভাবে ইনক্রিমেন্ট হবে তাও বলে দিয়েছি। এরপর অপারেশন কি হবে সেটাও বলে দিয়েছি। তাই এটা একটা Imperative Traversing। + +### Declarative Traverse + +সাধারণত আমাদের ফর লুপ চালিয়ে জাভাস্ক্রিপ্টে কাজ করতে হয় না। যেহেতু জাভাস্ক্রিপ্ট একটা হাই লেভেল ল্যাঙ্গুয়েজ সেহেতু এর বিভিন্ন মেথড আছে, যেগুলো ব্যবহার করে আমরা ডিক্লারেটিভ ওয়েতে ট্রাভার্স করতে পারি। ফাংশন এবং মেথড কি এগুলো আমরা পরবর্তীতে জানবো। আমরা যেভাবে ইম্পারেটিভ ট্রাভার্স করেছিলাম সেভাবে যদি ডিক্লারেটিভ ওয়েতে করতে যায় তাহলে একটা সুন্দর মেথড আছে যার নাম `forEach`। আমরা একটু এই মেথড বুঝার চেষ্টা করি। + +```js +const numbers = [2, 5, 6, 7, 89, 100]; + +numbers.forEach(function () { + console.log('Hello World'); +}); +``` + +এখন এই প্রোগ্রাম রান করালে দেখা যাবে যে ছয়বার `Hello World` প্রিন্ট হবে। কেন ছয়বার কারণ `numbers` এর ইলেমেন্ট আছে ছয়টা। `forEach` এর কাজই হলো যতটা ইলেমেন্ট ততবার লুপ চলবে। `forEach` এর মধ্যে আর্গুমেন্ট আকারে একটা কলব্যাক ফাংশন পাস করবে। আমরা চাইলে ফাংশনটা `forEach` এর মধ্যে না লিখে বাইরে লিখে সেই ফাংশনের নামটাও পাস করে দিতে পারি। এখন ভিতরের ফাংশনটা কিন্তু আমরা কোথাও কল করিনি। তাহলে কিভাবে তা কল হলো? আমাদের জন্য `forEach` সেই ফাংশনটা কল করে রেখেছে কোনো না কোনোভাবে। এই কলব্যাক ফাংশনের মধ্যে প্যারামিটার আকারে কিছু না কিছু আছে। সেগুলো সব `arguments` নামক একটা ডাটা স্ট্রাকচারে স্টোর করা আছে। এটা অনেকটা অ্যারের মতো কাজ করে, কিন্তু অ্যারে না, এটা একটা ডিফারেন্ট টাইপের একটা ডাটা স্ট্রাকচার। একটা উদাহরণ দিলে সুন্দরভাবে বুঝা যাবে ব্যাপারটা। + +```js +const numbers = [2, 5, 6, 7, 89, 100]; + +numbers.forEach(function () { + console.log(arguments); +}); + +/* * Output +[Arguments] { '0': 2, '1': 0, '2': [ 2, 5, 6, 7, 89, 100 ] } +[Arguments] { '0': 5, '1': 1, '2': [ 2, 5, 6, 7, 89, 100 ] } +[Arguments] { '0': 6, '1': 2, '2': [ 2, 5, 6, 7, 89, 100 ] } +[Arguments] { '0': 7, '1': 3, '2': [ 2, 5, 6, 7, 89, 100 ] } +[Arguments] { '0': 89, '1': 4, '2': [ 2, 5, 6, 7, 89, 100 ] } +[Arguments] { '0': 100, '1': 5, '2': [ 2, 5, 6, 7, 89, 100 ] } +*/ +``` + +আউটপুট থেকে দেখা যাচ্ছে অবজেক্টের মধ্যে '0' এর মধ্যে আছে আমাদের অ্যারের প্রতিটা ভ্যালু, '1' এর মধ্যে আছে সেই সংশ্লিষ্ট ভ্যালুর ইনডেক্স নাম্বার এবং '2' এর মধ্যে আছে পুরো অ্যারে। তাহলে আমরা বুঝতে পারলাম, `forEach` এর মধ্যে আর্গুমেন্ট আকারে যে ফাংশনটা আছে তার ভিতর তিনটা প্যারামিটার আছে। যদি একটু আমরা চেক করে দেখি, + +```js +const numbers = [2, 5, 6, 7, 89, 100]; + +numbers.forEach(function (value, index, array) { + console.log(value, index, array); +}); + +/* * Output +2 0 [ 2, 5, 6, 7, 89, 100 ] +5 1 [ 2, 5, 6, 7, 89, 100 ] +6 2 [ 2, 5, 6, 7, 89, 100 ] +7 3 [ 2, 5, 6, 7, 89, 100 ] +89 4 [ 2, 5, 6, 7, 89, 100 ] +100 5 [ 2, 5, 6, 7, 89, 100 ] +*/ +``` + +দেখা যাচ্ছে আমরা যে আউটপুট পেয়েছিলাম আর্গুমেন্টস এর বেলায় ঠিক সেই আউটপুটই পেয়েছি। `arguments` অনেক কাজের। আপনি যখন কোনো লাইব্রেরি বা ফ্রেমওয়ার্ক নিয়ে কাজ করবেন তখন যদি কোনো মেথডের আর্গুমেন্ট জানার প্রয়োজন হয় সহজেই তা বের করে নিতে পারবেন। + +এবার আসি আবার `forEach` এর কথায়। এটা দিয়ে ফর লুপের যাবতীয় যা যা কাজ আমরা করি সবই করতে পারি। এবার আমরা ফর লুপ দিয়ে যোগফলের যে কাজটি করেছিলাম সেটা একটু `forEach` দিয়ে করে দেখি। + +```js +const numbers = [2, 5, 6, 7, 89, 100]; + +let sum = 0; +numbers.forEach(function (value) { + sum += value; +}); +console.log(sum); // 209 +``` + +একই রেজাল্ট পাবো আমরা। এখানে একটা কথা বলে রাখা দরকার, যদি আমাদের `value` ছাড়া আর কিছু না লাগে তবে ফাংশন প্যারামিটার হিসেবে শুধু `value` নিলেই হবে। কিন্তু আমার যদি শুধু `array` দরকার হয় তবে অবশ্যই `value, index, array` এভাবে লিখতে হবে। নাহয় প্রোগ্রাম ভুল আউটপুট দেখাবে। এবার যদি চাই আমরা শুধু জোড় ইলেমেন্টগুলো প্রিন্ট করবো সেটাও পারবো। + +```js +const numbers = [2, 5, 6, 7, 89, 100]; + +numbers.forEach(function (value) { + if (value % 2 === 0) { + console.log(value); + } +}); +``` + +এখানে `forEach` ফাংশন আমরা তৈরি করিনি। আমরা শুধু ব্যবহার করেছি। সুতরাং এটি একটি ডিক্লারেটিভ মেথড। এখন হয়তো অনেকেরই জানতে ইচ্ছা করছে `forEach` মেথডে কি এমন করা হয়েছে। যারা `forEach` সহ অ্যারে এবং অ্যারে মেথড সম্পর্কে জানতে আগ্রহী তারা স্ট্যাক লার্নারের এই [প্লেলিস্ট](https://youtube.com/playlist?list=PL_XxuZqN0xVDr08QgQHljCecWtA4jBLnS) দেখতে পারেন। + +এখন আমি চাইছি যে শুধু প্রথম ৪টা ইলেমেন্টের যোগফল বের করবো। সেটার জন্য আমাদের কি করতে হবে তাহলে? + +```js +const numbers = [2, 5, 6, 7, 89, 100]; + +let sum = 0; +numbers.forEach(function (value, index) { + if (index <= 3) { + sum += value; + } +}); +console.log(sum); +``` + +`forEach` মেথড মনে রাখার সহজ উপায় হলো, আমরা যে ফর লুপ লিখতাম সেটা আর লিখতে হবে না। সেটা `forEach` আমাদের জন্য করে দিয়েছে। শুধু আমাদের কাজ হচ্ছে যেটা আমরা লুপের বডিতে লিখতাম সেটা আমরা কলব্যাক ফাংশনের বডির মধ্যে লিখবো। + +ধরি আমাদের একটা অ্যারে আছে নিচের মতো। + +```js +const arr = [1, 2, 3, null, false, 4, 5, '', 'test', 6, 7]; +``` + +এখন আমরা চাইছি এখান থেকে নাম্বার ছাড়া বাকি যা আছে সেগুলো বাদ দিয়ে শুধু নাম্বারগুলো ফিল্টার করে নিতে। সেটা আমরা ডিক্লারেটিভ ওয়েতে করতে চাইছি না। আমরা চাইছি ইম্পেরেটিভ ওয়েতে করতে। কিভাবে করতে পারি? + +আমরা এভাবে শুরু করতে পারি। + +```js +const arr = [1, 2, 3, null, false, 4, 5, '', 'test', 6, 7]; + +for (let i = 0; i < arr.length; i++) { + if (typeof arr[i] !== 'number') { + arr[i] = undefined; + } +} + +console.log(arr); // [1, 2, 3, undefined, undefined, 4, 5, undefined, undefined, 6, 7]; +``` + +এখন এখানে সমস্যা হলো এই `undefined` গুলোকে কিভাবে আমরা বাদ দিবো। আমাদের অন্য ওয়েতে চিন্তা করতে হবে। আমরা এমন করতে পারি যে কোনো পজিশনে ইলেমেন্ট টাইপ যদি নাম্বার না হয় তাহলে আমরা পরবর্তী ভ্যালুকে অ্যাসাইন করে দিতে পারি। যদি আমরা স্টেপগুলো একটু দেখি তাহলে বোঝা যাবে। + +```js +// step 1: [1, 2, 3, false, 4, 5, '', 'test', 6, 7, undefined] +// step 2: [1, 2, 3, 4, 5, '', 'test', 6, 7, undefined, undefined] +// step 3: [1, 2, 3, 4, 5, 'test', 6, 7, undefined, undefined, undefined] +// step 4: [1, 2, 3, 4, 5, 6, 7, undefined, undefined, undefined, undefined] +``` + +এবার আমাদের আইডিয়াকে আমরা একটু কোডে রূপান্তরিত করে দেখি। + +```js +const arr = [1, 2, 3, null, false, 4, 5, '', 'test', 6, 7]; + +for (let i = 0; i < arr.length; i++) { + for (let j = i; j < arr.length - 1; j++) { + if (!arr[j] || typeof arr[j] !== 'number') { + arr[j] = arr[j + 1]; + arr[j + 1] = undefined; + } + } +} + +console.log(arr); // [1, 2, 3, 4, 5, 6, 7, undefined, undefined, undefined, undefined]; +``` + +আমরা তাহলে আমাদের স্টেপ ৪ পেয়ে গেলাম। এবার এখান থেকে `undefined` বাদ দিয়ে দিতে হবে। সেটার জন্য আমরা একটা কাজ করতে পারি। + +```js +const arr = [1, 2, 3, null, false, 4, 5, '', 'test', 6, 7]; + +count = 0; +for (let i = 0; i < arr.length; i++) { + for (let j = i; j < arr.length - 1; j++) { + if (!arr[j] || typeof arr[j] !== 'number') { + arr[j] = arr[j + 1]; + arr[j + 1] = undefined; + } + } + + if (arr[i] == undefined) { + count++; + } +} +arr.length -= count; + +console.log(arr); // [1, 2, 3, 4, 5, 6, 7]; +``` + +আমরা করেছি কি? যদি ইলেমেন্ট আনডিফাইন্ড হয় তাহলে কাউন্ট করে সেটা `count` ভ্যারিয়েবলের মধ্যে রাখবে। শেষে আমরা `arr.length` থেকে `count` বিয়োগ করে অ্যারের সাইজ কমিয়ে দিলেই `undefined` সব বাদ পড়ে যাবে। + +এবার একটু কোডটা এনালাইসিস করার চেষ্টা করি। আমরা ছোট একটা অ্যারে দিয়েই বুঝার চেষ্টা করি। + +```txt +const arr = [1, false, true, '', 2, 3] +When i = 0: + j = 0: + arr[0] = 1, which is a number + j = 1: + arr[1] = false, which is not a number + so, arr[1] = true + arr[2] = undefined + j = 2: + arr[2] = undefined + so arr[2] = '' + arr[3] = undefined + j = 3: + arr[3] = undefined + so arr[3] = 2 + arr[4] = undefined + j = 4: + arr[4] = undefined + so arr[4] = 3 + arr[5] = undefined + count = 1 +After completion of first loop the array becomes like this [1, true, '', 2, 3, undefined] +After completion of loop the array looks like this [1, 2, 3, undefined, undefined, undefined] and count will be 3. After subtraction count from arr.length (6) we found 3. So the array of length 3 will become like this [1, 2, 3] +``` + +এখন যদি এই কাজটা ইম্পেরেটিভ ওয়েতে না করে ডিক্লারেটিভ ওয়েতে করতাম তাহলে অনেক সহজে করতে পারতাম। + +```js +const arr = [1, 2, 3, null, false, 4, 5, '', 'test', 6, 7]; + +const filteredArray = arr.filter((val) => typeof val === 'number'); +console.log(filteredArray); +``` + +কিন্তু এই জায়গায় একটা সমস্যা আছে। কারণ `filter` মেথড বিহাইন্ড দ্য সীন একটা এক্সট্রা মেমোরি ব্যবহার করে। আমরা যখন ফ্রন্টএন্ড ডেভেলপমেন্ট করি তখন সাধারণত এতো জটিল ইম্পেরেটিভ ওয়েতে করি না। আমরা যে বিল্ট-ইন মেথড আছে সেগুলো ব্যবহার করি। তাই দেখা যায় যে অনেক সময় ডাটা যখন অনেক বেশি হবে তখন অ্যাপ্লিকেশন হ্যাং হয়ে যায়। এখন আমরা কি সবসময় ইম্পেরেটিভ মেথডেই কাজ করবো? বা কখন বুঝবো আমাকে ইম্পেরেটিভ ওয়েতে করতে হবে, কখন ডিক্লারেটিভ ওয়েতে? প্রথম কথা হচ্ছে ৯০-৯৫% সময়ই আমাদের বিল্ট-ইন মেথড ইউজ করে কাজ হয়ে যাবে। কিন্তু কিছু কিছু ক্ষেত্রে আমাদের অ্যাপ্লিকেশনের কমপ্লেক্সিটি এতো বেশি হয় সেসব ক্ষেত্রে আমাদের বিল্ট-ইন মেথডের বাইরে গিয়ে কাজ করতে হতে পারে। ধরেন আমাদের অ্যারেতে এখন জাস্ট নাম্বার, স্ট্রিং এসব ডাটা আছে। কিন্তু যদি এমন হয় যে প্রতিটি ইলেমেন্ট এক একটা জায়ান্ট অবজেক্ট এবং প্রতিটা অবজেক্টের সাইজ প্রায় এক এমবি করে (যদিও এক এমবি ডাটা বানানো অনেক কঠিন, তাও বুঝার সুবিধার্থে উদাহরণ দিচ্ছি), এরকম যদি ১০০ টা অবজেক্ট থাকে তাহলে মোট অ্যারের সাইজ হবে ১০০ এমবি। এখন যদি এই ১০০ এমবি ডাটার অপারেশনের জন্য আমার আরো ১০০ এমবি মেমোরি খরচ হয় তাহলে সেটা অনেক সমস্যা। তাই এই ক্ষেত্রে আমাদের সম্পূর্ণ ইম্পেরেটিভ ওয়েতে গিয়ে কাজ করতে হবে। যদি আমাদের এখানে মেমোরি কনস্ট্রেইন না থাকতো তাহলে আমরা ইম্পেরেটিভ ওয়েতেও অনেক সহজে এই কাজটা করতে পারতাম। + +```jsx +const arr = [1, 2, 3, null, false, 4, 5, '', 'test', 6, 7]; + +const newArr = []; +for (let i = 0; i < arr.length; i++) { + if (typeof arr[i] === 'number') { + newArr.push(arr[i]); + } +} +console.log(newArr); +``` + +ফ্রন্টএন্ড অ্যাপ্লিকেশন বানানোর সময় আমাদের খেয়াল রাখতে হবে একজন ইউজার ৬৪ জিবি র‍্যামের পিসিও ইউজ করতে পারে, আবার ২ জিবি র‍্যামের পিসিও ইউজ করতে পারে। ব্যাকএন্ডে যতো ডাটা থাকবে তার জন্য সার্ভার কস্ট আমি বা আমার কোম্পানি বহন করছে। কিন্তু যখন ব্যাপার ফ্রন্টএন্ডের তখন সেটা পুরোপুরি ইউজার কেন্দ্রিক। আমি চাইবো আমার অ্যাপ্লিকেশন যেন ৬৪ জিবি র‍্যামের পিসি থেকেও ইউজ করার যায়, ২ জিবি র‍্যামের পিসি থেকেও ইউজ করা যায় আবার মোবাইল থেকেও যেন ইউজ করা যায়। তাই অনেক ছোট ছোট বিষয় খেয়াল রেখে ফ্রন্টএন্ড ডেভেলপমেন্ট করতে হয়। এখানেই ফ্রন্টএন্ড ডেভেলপমেন্টের চ্যালেঞ্জ। + +> **অ্যারের পরবর্তী ধাপগুলো ভালভাবে বুঝার জন্য অবজেক্ট সম্পর্কে জানা থাকতে হবে। তাই আগে [Object Operations](#object-operations), [Function vs Method](#function-vs-method), [Array](#array), [Object Over Array](#object-over-array), [Comparison of object and array operation costs](#comparison-of-object-and-array-operation-costs) এই টপিকগুলো ভালভাবে পড়ে নিন। এরপর পরবর্তী ধাপে যান।** + +### Update + +আপডেটের ক্ষেত্রে ইম্পেরেটিভ ওয়েতে করার কোনো প্রয়োজন নেই। আপডেট অনেক সিম্পল। আমাদের যদি কোনো অ্যারের ইনডেক্স জানা থাকে তাহলে খুব সহজেই আমরা তার ডাটা আপডেট করে ফেলতে পারি। যেমন + +```js +const arr = [1, 2, 3, 4, 5]; + +arr[3] = 300; + +console.log(arr); // [1, 2, 3, 300, 5] +``` + +এখন যদি ইনডেক্স জানা না থাকে তাহলে প্রথমে আগে ইনডেক্স বের করে নিতে হবে। এরপর আপডেট করা যাবে। যেমন + +```js +const arr = [ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 4, value: 40 }, + { id: 5, value: 50 }, +]; + +const index = arr.findIndex((item) => item.id === 4); +arr[index].value = 400; + +console.log(arr); + +// [ +// { id: 1, value: 10 }, +// { id: 2, value: 20 }, +// { id: 3, value: 30 }, +// { id: 4, value: 400 }, +// { id: 5, value: 50 } +// ] +``` + +আবার ইনডেক্স বের না করেও আপডেট করা যায়। সেক্ষেত্রে আমাদের `find` মেথড ব্যবহার করতে হবে। যেমন + +```js +const arr = [ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 4, value: 40 }, + { id: 5, value: 50 }, +]; + +const obj = arr.find((val) => val.id === 4); +obj.value = 400; + +console.log(obj); // { id: 4, value: 400 } +console.log(arr); + +/* +[ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 4, value: 400 }, + { id: 5, value: 50 } +] +*/ +``` + +এখানে দেখা যাচ্ছে আমি যদি `obj` এর ভ্যালু পরিবর্তন করি তাহলে `arr` এর ভ্যালুও পরিবর্তন হবে। কারণ হলো আমরা এখানে যেভাবে অ্যারে দেখতে পাচ্ছি আসলে তা সেরকম নাই। আমরা যতোই ডাটা রাখি অ্যারেতে জাস্ট অ্যারের মধ্যে কয়েকটা অ্যাড্রেস থাকে। ঐ ডাটাগুলোর অ্যড্রেস। মানে ঐ ডাটাগুলো যে অ্যাড্রেসে থাকে তা অ্যারে ভ্যারিয়েবলের মধ্যে জমা থাকে। আমরা যখন `obj` এর মধ্যে ফাইন্ড করছি তখন অ্যারের ঐ অ্যাড্রেসকে নিয়ে আসছি। তাই অ্যাড্রেস যেখানেই চেইঞ্জ করি না কেন তা অরিজিনাল অ্যারেতেও চেইঞ্জ হয়ে যাচ্ছে। এটা হচ্ছে মিউটেশন। আর `find` মেথড মিউটেবল। + +এখন একটা উদাহরণ দেখি। + +```js +const arr = [ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 4, value: 40 }, + { id: 5, value: 50 }, +]; + +const obj = arr.find((val) => val.id === 4); +obj.value = 400; + +console.log(arr[3] === obj); // true + +const a = { a: 10 }; +const b = { a: 10 }; +const c = a; +console.log(a === c); // true +console.log(a === b); // false +``` + +যেকোনো বিগিনারের কাছে এটা পুরাই কনফিউশন সৃষ্টি করবে। যখন obj কিছু find করে নিয়ে আসে তখন আসলে অ্যারের রেফারেন্সটা নিয়ে আসে। তাই obj এবং arr[3] এর রেফারেন্স একই এজন্যই সেটা `true` আউটপুট দিয়েছে। একই ভাবে c আর a রেফারেন্স একই। তাই সেটা true দিয়েছে। কিন্তু a আর b এর রেফারেন্স সম্পূর্ণ আলাদা। দুইটা অবজেক্টে যতই সেইম ভ্যালু থাক, দুইটা অবেজক্টের রেফারেন্স কখনও এক হবে না। দুইটা বিল্ডিং দেখতে যতোই একই হোক, দুইটা বিল্ডিং এর অ্যাড্রেস কখনও একই হবে না। এক্ষেত্রেও তাই। এই কারণে শেষের কন্ডিশন false দিয়েছে। + +### Delete + +এবার আমরা অ্যারে থেকে কিভাবে কোনো ডাটা ডিলিট করতে হয় তা দেখবো। ইম্পেরেটিভ ওয়েতে কিভাবে ডাটা ডিলিট করতে হয় তা আমরা গেই দেখেছি অ্যারে ট্রাভার্সের উদাহরণে। এখানে আমরা দুইটা মেথড ইউজ করে ডিলিট করবো। `splice` and `filter`. এদের মধ্যে পার্থক্য হলো splice মেথড মিউটেবল এবং filter ইমমিউটেবল। কিভাবে আমরা একটু দেখি। + +```js +const arr = [ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 4, value: 40 }, + { id: 5, value: 50 }, +]; + +const index = arr.findIndex((item) => item.id === 4); +const arr1 = arr.splice(index, 1); + +console.log(arr1); // [ { id: 4, value: 40 } ] +console.log(arr); +/* [ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 5, value: 50 } +] */ +``` + +এখানে দেখা যাচ্ছে splice মেথড সরাসরি অরিজিনাল অ্যারে থেকে ডাটা ডিলিট করে দিয়েছে। তার মানে এখানে মিউটেশন হয়েছে। + +```js +const arr = [ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 4, value: 40 }, + { id: 5, value: 50 }, +]; + +const arr2 = arr.filter((item) => item.id !== 4); + +console.log(arr2); +/* +[ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 5, value: 50 } +] +*/ +console.log(arr); +/* +[ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, + { id: 4, value: 40 }, + { id: 5, value: 50 } +] +*/ +``` + +এখানে অরিজিনাল অ্যারে যেমন ছিল তেমনই আছে। কিন্তু ফিল্টার করার পর ফিল্টার মেথড নতুন একটা অ্যারে দিয়েছে যেখানে যেটা ডিলিট করতে চেয়েছিলাম সেটা নেই। তার মানে দাঁড়ালো filter মেথড ইমমিউটেবল। + +### Mutation + +মিউটেশন নিয়ে অলরেডি আলোচনা হয়েছে। আশা করি ব্যাপারটা সবাই বুঝতে পেরেছেন। + +### Map + +ম্যাপ সাধারণত অরিজিনাল অ্যারের ক্লোন ভার্সন তৈরি করে। যদি অরিজিনাল অ্যারেতে ১০টা ডাটা থাকে তাহলে নতুন অ্যারেতেও ১০টা ডাটা থাকবে। এখন সে ডাটা একই হতে পারে বা ডিফারেন্ট হতে পারে। যেমন + +```js +const numbers = [1, 2, 3, 4]; +const strs = numbers.map((v) => v.toString()); +console.log(strs); +``` + +সব নাম্বারের স্ট্রিং ভার্সন সে আউটপুট দিবে। একটা জিনিস মাথায় রাখতে হবে ম্যাপ করার পর অ্যারের লেংথের কোনো পরিবর্তন হবে না। শুধু ডাটা পরিবর্তন হবে। ডাটার সংখ্যা একই থাকবে। + +### Filter + +ফিল্টারের কাজ আমরা একটা অ্যারে থেকে যে যে ডাটা চাইছি তা ফিল্টার করে দেয়া। ধরেন আমাদের কাছে একটা অ্যারে আছে। + +```js +const numbers = [1, 2, 3, 4, false, '', NaN, 5, 6]; +``` + +আমরা চাইছি এখান থেকে সমস্ত falsy value বাদ দিয়ে শুধু truthy ভ্যালু নিবো। সেক্ষেত্রে ফিল্টার মেথড আমাদের ব্যবহার করতে হবে। + +```js +const filteredArr = numbers.filter((v) => v); +console.log(filteredArr); +``` + +এক্ষেত্রে সকল truthy value রিটার্ন করে দিবে। কিন্তু এমন কিছু সিচুয়েশন আসবে যখন আমি truthy value চাইছি কিন্তু রিটার্ন করতে পারবো না সেক্ষেত্রে v এর আগে দুইটা !! বসিয়ে দিলেই truthy value পেয়ে যাবো। + +### Reduce + +আমরা একটু নিচের উদাহরণটা দেখি। + +```js +const numbers = [1, 2, 3, 4, false, '', NaN, 5, 6]; +const filteredArr = numbers.filter((v) => v); +const strs = filteredArr.map((v) => v.toString()); +console.log(strs); +``` + +এক্ষেত্রে কিছু অসুবিধা আছে। যখন ফিল্টার হচ্ছে তখন n সংখ্যকবার সে ট্রাভার্স হচ্ছে। আবার যখন ম্যাপ হচ্ছে তখনও আবার ট্রাভার্স হচ্ছে। এতে করে টাইম কমপ্লেক্সিটি বেড়ে যাচ্ছে। এটা চেইন করেও করা যায়। + +```js +const numbers = [1, 2, 3, 4, false, '', NaN, 5, 6]; +const filteredArr = numbers.filter((v) => v).map((v) => v.toString()); +console.log(strs); +``` + +এক্ষেত্রে টাইম কমপ্লেক্সিটি কিছুটা কমলেও পুরোপুরি এফিশিয়েন্ট না। সেজন্য আমাদের যেতে হবে reduce মেথডের কাছে। + +ইউটিউবে আমরা যে সকল টিউটোরিয়াল দেখতে পাই তাতে reduce দিয়ে একটা কাজই ঘুরেফিরে করা হয়। সেটা হলো যোগ করা। + +```jsx +const numbers = [1, 2, 3, 4, 5, 6]; +const sum = numbers.reduce((a, b) => a + b); +console.log(sum); +``` + +কিন্তু reduce is way more powerful than summation. reduce এত পাওয়ারফুল যে তা কল্পনা করা যায় না। reduce ঠিকমতো বুঝলে ম্যাপ, ফিল্টার নিয়ে কাজ না করে reduce নিয়েই কাজ করে ফেলা যায়। ম্যাপ আমাদের রিটার্ন করে একই দৈর্ঘ্যের একটা নতুন অ্যারে। ফিল্টার ফিল্টারড ভ্যালুর অ্যারে রিটার্ন করে। এর দৈর্ঘ্য অরিজিনাল অ্যারের সমান হতেও পারে, নাও পারে। কিন্তু রিডিউস কি যে রিটার্ন করবে তা কেউ জানে না। শুধু আমরা জানবো। এখানে স্ট্রিং, নাম্বার, বুলিয়ান ইত্যাদি যেকোনো সম্ভাব্য ভ্যালুই এটা রিটার্ন করতে পারে। + +আমরা একটু reduce এর স্ট্রাকচারটা দেখি + +```js +numbers.reduce((acc, cur) => { + return acc; +}, ''); +``` + +প্রথম প্যারামিটার হিসেবে আমরা দিয়েছি acc (accumulator / previous value) এবং দ্বিতীয় ভ্যালু হিসেবে দিয়েছি cur (current value)। acc, cur এর পর আমরা চাইলে ইনডেক্স দিতে পারি, চাইলে পুরো অ্যারে দিতে পারি কিন্তু আমাদের সেটা দরকার নেই। reduce মেথডের সুবিধা হলো এখানে আমরা একটা ইনিশিয়াল ভ্যালু প্রোভাইড করতে পারি। '' এর জায়গায় খালি অবজেক্ট {}, খালি অ্যারে [], শূন্য যেকোনো কিছু বসাতে পারি। সেটা আমরা কি চাইছি তার উপর নির্ভর করবে। এর মানে হলো বর্তমানে acc এর ভ্যালু ঐ ইনিশিয়ালাইজার হিসেবে যেটা দিবো সেটা। দিন শেষে আমরা আমাদের acc কে রিটার্ন করবো। যাই করি না কেন আমরা reduce মেথডে acc কেই রিটার্ন করবো। এখন আমরা চাইছি `const numbers = [1, 2, 3, 4, false, '', NaN, 5, 6];` এটা থেকে আমরা `1234falseNaN56` রিটার্ন করতে। সেটা করতে আমরা reduce মেথডের সাহায্য নিবো। + +```js +const numbers = [1, 2, 3, 4, false, '', NaN, 5, 6]; +const result = numbers.reduce((acc, cur) => { + acc += cur.toString(); + return acc; +}, ''); + +console.log(result); // 1234falseNaN56 +``` + +আমরা করেছি কি এখানে? acc এর ভ্যালু আমরা ধরে নিয়েছি ''। এরপর ওটার সাথে cur এর toString যোগ করে দিয়েছি। এবং আমাদের রেজাল্টটাকে আমরা একটা ভ্যারিয়েবলের মধ্যে রেখেছি। এরপর যখন আউটপুট দিলো দেখা গেলো আমরা যেটা চাইছি সেটাই পেয়ে গেছি। + +এখন আমরা চাইছি এই অ্যারে থেকে শুধু truthy values নিবো। কোনো falsy ভ্যালু নিবো না। সেক্ষেত্রে আমরা একটা কন্ডিশন বসিয়ে দিতে পারি। + +```js +const numbers = [1, 2, 3, 4, false, '', NaN, 5, 6]; +const result = numbers.reduce((acc, cur) => { + if (cur) { + acc += cur.toString(); + } + return acc; +}, ''); + +console.log(result); // 123456 +``` + +আমরা যদি চাই প্রতিটার শেষে কমা (,) যোগ করবো সেটাও করতে পারি। + +```js +const numbers = [1, 2, 3, 4, false, '', NaN, 5, 6]; +const result = numbers.reduce((acc, cur, index) => { + if (cur) { + acc += cur.toString() + (index < numbers.length - 1 ? ', ' : ''); + } + return acc; +}, ''); + +console.log(result); // 1, 2, 3, 4, 5, 6 +``` + +আমরা চাইলে অ্যারের একটা শেইপও দিতে পারি। যেমন + +```js +const numbers = [1, 2, 3, 4, false, '', NaN, 5, 6]; +const result = numbers.reduce((acc, cur, i) => { + if (i === 0) { + acc += '['; + } + if (cur) { + acc += cur.toString() + (i < numbers.length - 1 ? ', ' : ''); + } + if (i === numbers.length - 1) { + acc += ']'; + } + return acc; +}, ''); +console.log(result); // [1, 2, 3, 4, 5, 6] +``` + +তাহলে আমরা reduce এর পাওয়ারটা বুঝতে পারছি কিছুটা। এটা গেলো এক ধরণের পাওয়ার। আরো অনেক পাওয়ার আছে reduce মেথডের। যেমন এখন আমরা acc স্ট্রিং হিসেবে চাইছি না। আমরা চাইছি সকল truthy ভ্যালুর একটা অ্যারে। সেটাও reduce দিয়ে করা যায়। + +```js +const numbers = [1, 2, 3, 4, false, '', NaN, 5, 6]; +const result = numbers.reduce((acc, cur) => { + if (cur) { + acc.push(cur.toString()); + } + return acc; +}, []); +console.log(result); // [ '1', '2', '3', '4', '5', '6' ] +``` + +এখানে আমরা acc হিসেবে একটা ফাঁকা অ্যারে নিয়েছি। এরপর একটা কন্ডিশন লিখেছি truthy ভ্যালু পাওয়ার জন্য। তারপর সেই কন্ডিশন যে সকল ভ্যালুর পূরণ করবে তাদের toString ভার্সন আমরা acc এর মধ্যে push করে দিবো যেহেতু acc একটা অ্যারে। আমরা একই রেজাল্ট পাচ্ছি আরো বেটার সল্যুশনের মাধ্যমে। + +আমরা একটু map/filter অপারেশনের সাথে reduce অপারেশনের টাইম কমপ্লেক্সিটি তুলনা করে দেখি। + +```js +const arr = []; +for (let i = 1; i < 5000000; i++) { + arr.push(i); +} + +console.time('not-optimized'); +arr.filter((item) => item % 2 === 0).map((item) => item * 2); +console.timeEnd('not-optimized'); // not-optimized: 325.853ms + +console.time('optimized'); +arr.reduce((acc, cur) => { + if (cur % 2 === 0) { + acc.push(cur * 2); + } + return acc; +}, []); +console.timeEnd('optimized'); // optimized: 198.256ms +``` + +তাহলে দেখা যাচ্ছে reduce method অনেক অপটিমাইজড। এবার আমরা একটু reduce মেথডের ইমপ্লিমেন্টেশনটা দেখি। আমরা আমদের reduce ফুঞ্চতিওন বানিয়ে ফেলতে পারি। যেহেতু আমরা প্রোটোটাইপ নিয়ে আলোচনা করিনি তাই মেথড বানাবো না। আমরা জাস্ট একটা ফাংশন বানাবো। + +```js +function myReduce(arr, cb, init) { + let acc = init; + for (let i = 0; i < arr.length; i++) { + acc = cb(acc, arr[i], i, arr); + } + return acc; +} +``` + +এটাই আমাদের reduce ফাংশন। এখানে কি করেছি একটু ব্যাখ্যা করা যাক। আমরা তিনটা প্যারামিটার নিয়েছি। প্রথম প্যারামিটার হিসেবে থাকবে একটা অ্যারে। দ্বিতীয় প্যারামিটার হিসেবে থাকবে একটা কলব্যাক ফাংশন। আর তৃতীয় প্যারামিটার হিসেবে থাকবে আমাদের ইনিশিয়ালাজার। আমরা যে ইনিশিয়ালাইজার ব্যবহার করেছিলাম reduce মেথডে সেটা। এখন আমরা আমাদের acc হিসেবে init নিয়ে নিলাম। এরপর লুপ চালালাম। লুপের মধ্যে acc আপডেট হচ্ছে কলব্যাক ফাংশন অনুযায়ী। সেই কলব্যাক ফাংশনের প্যারামিটার হিসেবে থাকছে acc, অ্যারের ইলেমেন্ট, ইনডেক্স আর আমাদের অ্যারে। আর এই ফাংশন রিটার্ন করবে আমাদের acc। এবার একটু আমাদের ফাংশনটা টেস্ট করে দেখি। + +```js +const sum = myReduce([1, 2, 3, 4], (a, b) => a + b, 0); +console.log(sum); // 10 + +const arr = [1, 2, '', false, 3, NaN, false, 4, 5, NaN, 6]; +const result = myReduce( + arr, + (acc, cur) => { + if (cur) { + acc.push(cur ** 2); + } + return acc; + }, + [] +); +console.log(result); // [1, 4, 9, 16, 25, 36] +``` + +How amazing is this! জাভাস্ক্রিপ্টের যতোই গভীরে যাবেন এর মজাটা ততোই পাবেন। আমরা আমাদের reduce ফাংশন বানিয়ে সেটা নিয়ে কাজও করে ফেললাম। আর এটাও জানলাম behind the scene redcuce মেথড কিভাবে কাজ করে। + +আমরা আরেকটা উদাহরণ দেখি reduce এর। তার জন্য আমাদের axios প্যাকেজটা ইনস্টল করে নেয়া লাগবে। আমরা ইনস্টল করে নিলাম। এখন আমরা [json placeholder](https://jsonplaceholder.typicode.com/posts) এই সাইটে ঢুকলে কিছু ডামী ডাটা পাবো পোস্টের। খেয়াল করলে দেখবো এই ডাটা দেয়া আছে অ্যারে হিসেবে। কিন্তু আমার ট্রাভার্সের চেয়ে গুরুত্বপূর্ণ হলো আপডেট ও ডিলিট করা। ব্যাকএন্ড ডেভেলপার তার সুবিধামতো অ্যারেতে দিয়ে দিলেও আমাদের নিজেদের প্রয়োজনে তা অবজেক্টে রূপান্তরিত করে নেয়া লাগবে। এখানে আমাদের body প্রোপার্টিজ প্রয়োজন নেই। আমাদের দরকার userId, id ও title। আর আমার এতো ডাটার প্রয়োজন নেই আমাদের প্রথম ১০টা ডাটা হলেই হয়ে যাবে। চলুন দেখি। + +```js +const axios = require('axios').default; +const url = 'https://jsonplaceholder.typicode.com/posts'; + +async function getData() { + const { data } = await axios.get(url); + const result = data.slice(0, 10).map((item) => { + return { + userId: item.userId, + id: item.id, + title: item.title, + }; + }); + return result; +} + +getData() + .then((data) => console.log(data)) + .catch((e) => console.log(e)); + +/* +[ + { + userId: 1, + id: 1, + title: + 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit', + }, + { userId: 1, id: 2, title: 'qui est esse' }, + { + userId: 1, + id: 3, + title: 'ea molestias quasi exercitationem repellat qui ipsa sit aut', + }, + { userId: 1, id: 4, title: 'eum et est occaecati' }, + { userId: 1, id: 5, title: 'nesciunt quas odio' }, + { userId: 1, id: 6, title: 'dolorem eum magni eos aperiam quia' }, + { userId: 1, id: 7, title: 'magnam facilis autem' }, + { userId: 1, id: 8, title: 'dolorem dolore est ipsam' }, + { + userId: 1, + id: 9, + title: 'nesciunt iure omnis dolorem tempora et accusantium', + }, + { userId: 1, id: 10, title: 'optio molestias id quia eum' }, +]; +*/ +``` + +আমরা map ব্যবহার করে প্রথম ১০টি ডাটা পেয়ে গেলাম। এবং বডিও আমরা বাদ দিয়ে দিলাম। কিন্তু এখনও এটা অ্যারে রিটার্ন করছে। map করলে কখনও আমরা অবজেক্ট রিটার্ন করতে পারবো না। কারণ map সবসময় অ্যারেই রিটার্ন করে। এবার আমরা একটু reduce নিয়ে কাজ করি। কারণ reduce এ আমরা কি টাইপের ডাটা চাই তা ইনিশিয়ালাইজের মাধ্যমে দিয়ে দিতে পারি। + +```js +const axios = require('axios').default; +const url = 'https://jsonplaceholder.typicode.com/posts'; + +async function getData() { + const { data } = await axios.get(url); + const result = data.slice(0, 10).reduce((acc, cur) => { + acc[cur.id] = { + ...cur, + }; + delete acc[cur.id].body; + return acc; + }, {}); + return result; +} + +getData() + .then((data) => console.log(data)) + .catch((e) => console.log(e)); + +/* +{ + '1': { + userId: 1, + id: 1, + title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit' + }, + '2': { userId: 1, id: 2, title: 'qui est esse' }, + '3': { + userId: 1, + id: 3, + title: 'ea molestias quasi exercitationem repellat qui ipsa sit aut' + }, + '4': { userId: 1, id: 4, title: 'eum et est occaecati' }, + '5': { userId: 1, id: 5, title: 'nesciunt quas odio' }, + '6': { userId: 1, id: 6, title: 'dolorem eum magni eos aperiam quia' }, + '7': { userId: 1, id: 7, title: 'magnam facilis autem' }, + '8': { userId: 1, id: 8, title: 'dolorem dolore est ipsam' }, + '9': { + userId: 1, + id: 9, + title: 'nesciunt iure omnis dolorem tempora et accusantium' + }, + '10': { userId: 1, id: 10, title: 'optio molestias id quia eum' } +} +*/ +``` + +আমরা এখানে acc হিসেবে নিয়েছি একটা ফাঁকা অবজেক্ট ({})। সেই অবজেক্টের কী হিসেবে থাকবে current ভ্যালুর আইডি। আমরা সেই আইডি ধরে সব current ভ্যালু অবজেক্টে স্টোর করে দিলাম। এখন আমরা তো বডি চাই না। তাই পরের লাইনে সিমপ্লি delete এর মাধ্যমে বডি ডিলিট করে দিলাম। আর দিনশেষে তো acc ই রিটার্ন করবে। সব শেষে যখন রান করালাম, ওয়াও, আমাদের অবজেক্ট আমরা পেয়ে গেলাম। reduce এর পাওয়ার অন্য লেভেলের। এর পাওয়ার বলে শেষ করা যায় না। + +লাস্ট আরেকটা এক্সাম্পল আমরা দেখি এই reduce মেথডের। ধরুন আমাদের কাছে একটা অ্যারে আছে নামের। + +```js +const names = [ + 'Ayman', + 'Abu Rayhan', + 'Anik', + 'Elias Emon', + 'Engr. Sabbir', + 'Fahim Faisal', + 'Feroz Khan', + 'Habib', + 'HM Azizul', + 'Hridoy Saha', + 'Jahid Hassan', + 'Johir', + 'Md Al-Amin', + 'Md Arafatul', + 'Md Ashraful', + 'Parvez', +]; +``` + +আমরা এটাকে নিচের মতো করে আউটপুট পেতে চাইছি। + +```txt +----------- A ----------- +Ayman +Abu Rayhan +Anik + +----------- E ----------- +Elias Emon +Engr. Sabbir + +----------- F ----------- +Fahim Faisal +Feroz Khan + +----------- H ----------- +Habib +HM Azizul +Hridoy Saha + +----------- J ----------- +Jahid Hassan +Johir + +----------- M ----------- +Md Al-Amin +Md Arafatul +Md Ashraful + +----------- P ----------- +Parvez +``` + +এটা আমরা কিভাবে পেতে পারি। আমাদের আছে অ্যারে। আমরা যদি এই কাজটাকে নিচের স্ট্রাকচার হিসেবে কল্পনা করি তাহলে অনেক সহজ হয়ে যাবে। + +```js +const namesGroup = { + A: ['Ayman', 'Abu Rayhan', 'Anik'], + E: ['Elias Emon', 'Engr. Sabbir'], + F: ['Fahim Faisal', 'Feroz Khan'], +}; +``` + +এখন অ্যারে থেকে আমাদের এভাবে অবজেক্টে পরিণত করতে হবে। আর এই কাজটা করতে পারে reduce. তাহলে চলুন করা যাক। + +```js +const namesGrouped = names.reduce((acc, cur) => { + const firstLetter = cur[0].toUpperCase(); + if (firstLetter in acc) { + acc[firstLetter].push(cur); + } else { + acc[firstLetter] = [cur]; + } + return acc; +}, {}); +console.log(namesGrouped); + +/* +{ + A: [ 'Ayman', 'Abu Rayhan', 'Anik' ], + E: [ 'Elias Emon', 'Engr. Sabbir' ], + F: [ 'Fahim Faisal', 'Feroz Khan' ], + H: [ 'Habib', 'HM Azizul', 'Hridoy Saha' ], + J: [ 'Jahid Hassan', 'Johir' ], + M: [ 'Md Al-Amin', 'Md Arafatul', 'Md Ashraful' ], + P: [ 'Parvez' ] +} +*/ +``` + +আমরা প্রথমে আমাদের acc কে একটা ফাঁকা অবজেক্ট হিসেবে নিয়ে নিলাম। এরপর আমরা প্রথম লেটার ধরে চেক করবো তা acc তে আছে কিনা। যদি থাকে কি করবো আর না থাকলে কি করবো। তাহলে প্রথমে আমরা current ভ্যালুর প্রথম লেটারের আপারকেইস নিয়ে একটা ভ্যারিয়েবলে স্টোর করে রাখলাম। এবার একটা কন্ডিশন লিখলাম। যদি firstLetter acc এর মধ্যে না থাকে firstLetter দিয়ে একটা কী তৈরি করবে এবং ঐ কী এর মধ্যে current ভ্যালুর একটা অ্যারে নিবে। যদি firstLetter acc এর মধ্যে থাকে তাহলে জাস্ট কারেন্ট ভ্যালুর যে অ্যারে তাতে push করে দিবে। এবার যদি আমরা একটু আউটপুট দেখি তাহলে দেখবো আমরা যে স্ট্রাকচারটা কল্পনা করেছিলাম সেটা পেয়ে গেছি। এবার এখান থেকে আমাদের রিকোয়ার্ড আউটপুট কিভাবে প্রিন্ট করবো, যেটা শুরুতে দেখিয়েছিলাম, তা একটু দেখি। + +```js +Object.keys(namesGrouped).forEach((groupKey) => { + console.log('-----------', groupKey, '-----------'); + namesGrouped[groupKey].forEach((name) => console.log(name)); + console.log(); +}); +``` + +এটা আশা করি বুঝানোর কিছু নেই। সিম্পল forEach মেথড যা আগে দেখেছিলাম। রান করলে দেখবেন আমাদের ডিজায়ার্ড আউটপুট আমরা পেয়ে গেছি। + +যদি আমাদের filter, map, reduce জানা থাকে ভালভাবে তাহলে অন্যান্য ডাটা স্ট্রাকচার এবং অ্যালগরিদম ব্যবহার না করেও আমরা কিছু কিছু ক্ষেত্রে অপটিমাইজড অ্যাপ্লিকেশন বানিয়ে ফেলতে পারবো। + +## Object Deep Dive + +### Object Operations + +আমাদের চারপাশে আমরা যা দেখি তাই অবজেক্ট। ধরে আমাদের সামনে একটি মাইক্রোফোন আছে। এটাও একটা অবজেক্ট। কিভাবে চলুন একটু দেখি। + +```js +const microphone = { + brand: 'Fifine', + indictor: true, + price: 8000, + color: 'Black', + startRecording() { + console.log('recording started'); + }, + stopRecording() { + console.log('recording stopped'); + }, +}; +``` + +যখন আমাদের কোনো একটা বিষয় বা বস্তুকে রিপ্রেজেন্ট করার জন্য একাধিক ইনফরমেশন দরকার, তখনই আমাদের প্রয়োজন অবজেক্ট। একটা ইনফরমেশন হলে আমরা ভ্যারিয়েবল নিয়ে কাজ সেরে ফেলতে পারতাম। কিন্তু যেহেতু একের অধিক তাই আমাকে ঐ বিষয় বা বস্তু রিপ্রেজেন্ট করার জন্য প্রয়োজন অবজেক্ট। সেইম জিনিস জাভাতে বলে ক্লাস, পাইথনে বলে ডিকশনারি, সি তে সেটা হলো স্ট্রাকচার। এখন অবজেক্ট মানেই অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিং না। অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিং হলো এই অবজেক্টকেই কিভাবে সুন্দর করে অর্গানাইজড ওয়েতে রিপ্রেজেন্ট করা যায় সেটার থিওরাম হচ্ছে অবজেক্ট অরিয়েন্টেড প্রোগ্রামিং। এই টার্মটা আমাদের এখন প্রয়োজন নেই। আমরা বেসিক অবজেক্ট নিয়ে কথা বলছি, তাই ফোকাসটা আপাতত অবজেক্টের দিকেই দিই। + +আমরা জানি যে, অবজেক্টের মধ্যে এর অনেকগুলো প্রোপার্টি রাখতে পারি। অবজেক্টের প্রোপার্টিজকে দুই ভাগে ভাগ করা যায়। যথাঃ + +1. Noun / Adjective (State/data/property/field) - যে প্রোপার্টি দ্বারা আমাদের ডাটা রিপ্রেজেন্ট করতে পারি সেগুলোই এর আলোচ্য বিষয়। উপরের উদাহরণে brand, indicator, price, color এগুলো সবই প্রোপার্টি। কারণ এগুলো ডাটা রিপ্রেজেন্ট করছে। এই ডাটাগুলো স্ট্রিং, নাম্বার, বুলিয়ান যেকোনো ডাটা টাইপের হতে পারে। +2. Verb (functionalities -> start, stop) - যেমন আমাদের মাইক্রোফোনে কিছু ফাংশনালিটিজ থাকে। যেমন, start button, stop button, recording button etc. যেমন উপরের উদাহরণে startRecording, stopRecording। + +তাহলে অবজেক্টের দুইটা অংশের একটা আমাদের ডাটাকে রিপ্রেজেন্ট করবে, আরেকটা অংশ ডাটার সাথে রিলেটেড কাজগুলো করবে। এই দুইটা অংশ মিলেই আমাদের একটা অবজেক্ট তৈরি হয়। + +এখন এখানে যেসব প্রোপার্টি আমরা লিখলাম এর বাইরেও অনেক প্রোপার্টি আছে যেগুলো হিডেন আছে। যেমন আমরা যদি লিখি `microphone.toString()` তাহলে আউটপুট আসবে `[object Object]`। কিন্তু `toString` মেথড তো আমরা এখানে কোথাও লিখিনি। তাহলে এটা আসলো কোথা থেকে। এটা এসেছে `Object` থেকে। এই `Object` কে বলা হয় অবজক্ট কন্সট্রাকটর। + +আমরা যেভাবে অবজেক্ট তৈরি করেছিলাম সেটা ছাড়াও অন্যভাবে অবজেক্ট তৈরি করা যায়। আমরা একটু সেই প্রসেসটাও দেখি। + +```js +const testObj = new Object(); +testObj.name = 'Test Object'; +testObj.time = new Date(); +console.log(testObj); // { name: 'Test Object', time: 2022-06-16T07:09:01.373Z } +``` + +আউটপুটে আমরা দেখতে পাচ্ছি একটা অবজেক্ট ক্রিয়েট হয়ে গিয়েছে। তার মানে আমরা দুইভাবেই অবজেক্ট ক্রিয়েট করতে পারি। প্রথমে যেভাবে তৈরি করেছি সেটাকে বলে `Object Literal` এবং পরে যেভাবে তৈরি করেছি সেটাকে বলে `Constructor Function`। আমরা যেভাবেই তৈরি করি না কেন সবকিছুর পিছনে ঐ `Object` কনস্ট্রাক্টরই কাজ করছে। এই `Object` এর মধ্যে কিছু ্ প্রোপার্টিজ আছে যা আমরা দুনিয়ায় যতো অবজেক্টই বানাবো সবকিছুতে ইনহেরিট হয়ে যাবে। আমরা একটু সেসব প্রোপার্টিজ দেখার চেষ্টা করি। এর জন্য আমাদের একটু ব্রাউজারের কনসোলে যেতে হবে। নিচের স্ক্রিনশটটি একটু খেয়াল করুন আপনারা। + +![Object methods](./Screenshot_1.png) + +প্রথমে আছে কনস্ট্রাকটর। আমরা `Object` এর আগে `new` লাগিয়ে যে অবজেক্ট তৈরি করেছিলাম সেটাকে সেজন্য কন্সট্রাক্টর বলে। এরপর আছে `hasOwnProperty` এটা দিয়ে আমরা কোনো প্রোপার্টি ঐ অবজেক্টের নিজস্ব প্রোপার্টি কিনা তা চেক করতে পারবো। এছাড়াও `toString`, `valueOf`, `toLocaleString` ইত্যাদি প্রোপার্টিজ আছে। যেগুলো আমরা অবজেক্টে ডিফাইন না করলেও তারা প্রয়োজনে সেই মেথডগুলো ব্যবহার করতে পারবে। এগুলো মূলত আমরা যখন অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিং করতে যাবো তখন এসব দরকার পড়বে। এখন অবশ্য ES6 আসার পরে অতো ডিপলি অবজেক্ট অরিয়েন্টেড প্রোগ্রামিং করার দরকার পড়ে না। তারপরও যতটুকু দরকার আমরা শিখে নিবো।এই মুহূর্তে সেটা নিয়ে মাথা না ঘামালেও চলবে। আমরা যদি `Oboject` লিখে একটা ডট (.) দিই তাহলে অনেক প্রোপার্টিজ আসবে। এখন এদের মধ্যে কোন কোন প্রোপার্টিজ ইনহেরিট হবে বা এক্সটেন্ডেড হবে এবং কোন কোন প্রোপার্টিজ হবে না। উপরের ছবিটি খেয়াল করুন। প্রোটটাইপের মধ্যে যে সকল প্রোপার্টিজ আছে সেগুলো ইনহেরিট বা এক্সটেন্ডেড হবে। আর যেগুলো নেই সেগুলো হবে না। + +এই প্রোপার্টিজগুলোর মধ্যে আমরা একটু `freeze` প্রোপার্টিটা দেখি। ধরেন আমরা আমাদের microphone অবজেক্টে নতুন একটা প্রোপার্টি অ্যাড করতে চাইছি। তাহলে আমাদের নিচের কোডটা লিখতে হবে।] + +```js +const microphone = { + brand: 'Fifine', + indictor: true, + price: 8000, + color: 'Black', + startRecording() { + console.log('recording started'); + }, + stopRecording() { + console.log('recording stopped'); + }, +}; + +microphone.newProperty = 'New Property'; +console.log(microphone); +/* { + brand: 'Fifine', + indictor: true, + price: 8000, + color: 'Black', + startRecording: [Function: startRecording], + stopRecording: [Function: stopRecording], + newProperty: 'New Property' +} */ +``` + +কিন্তু অনেক সময় এমন অবজেক্ট নিয়ে আমরা কাজ করতে পারি যেখানে আমরা ডাটা এন্ট্রি রেস্ট্রিক্ট করে দিতে চাইছি। সোজা কথায় আমরা এখানে ডাটা ইনপুট দিতে দিবো না। সেই ক্ষেত্রে `freeze` মেথডটা অনেক কাজে আসে। + +```js +const microphone = { + brand: 'Fifine', + indictor: true, + price: 8000, + color: 'Black', + startRecording() { + console.log('recording started'); + }, + stopRecording() { + console.log('recording stopped'); + }, +}; + +Object.freeze(microphone); +microphone.newProperty = 'New Property'; +console.log(microphone); +/* { + brand: 'Fifine', + indictor: true, + price: 8000, + color: 'Black', + startRecording: [Function: startRecording], + stopRecording: [Function: stopRecording], +} */ +``` + +খেয়াল করুন এখানে আমাদের অবজেক্ট কিন্তু আপডেট হয়নি। এই মেথড ব্যবহার করে আমরা অবজেক্টকে লক করে দিতে পারি। আরো দুইটা মেথড দেখি আমরা। একটা `keys` এবং অন্যটা `values`। + +```js +console.log(Object.keys(microphone)); // ['brand', 'indictor', 'price', 'color', 'startRecording', 'stopRecording']; +console.log(Object.values(microphone)); + +/* +[ + 'Fifine', + true, + 8000, + 'Black', + [Function: startRecording], + [Function: stopRecording] +] +*/ +``` + +`Object.keys()` অবজেক্টের সব keys অ্যারে আকারে রিটার্ন করবে এবং `Object.values()` অবজেক্টের সব values অ্যারে আকারে রিটার্ন করবে। এখন এগুলো আমাদের কি দরকার? আমরা তো এগুলো ছাড়াও লুপ চালিয়ে কী এবং ভ্যালু বের করে আনতে পারি এভাবে- + +```js +for (let k in microphone) { + console.log(k, microphone[k]); +} + +/* +brand Fifine +indictor true +price 8000 +color Black +startRecording [Function: startRecording] +stopRecording [Function: stopRecording] +*/ +``` + +এখানে ভ্যালু বের করার জন্য যে অবজেক্ট নোটেশন ব্যবহার করা হয়েছে তাকে বলে অ্যারে নোটেশন। অবজেক্ট থেকে ভ্যালু দুইটা নোটেশন ইউজ করে বের করা যায়। + +- Dot notation (microphone.brand) +- Array notation (microphone['brand]) + +যখন আমরা ডায়নামিক্যালি কোনো কী নিবো তখন আমরা জানি না সেটা কিরকম। তাই আমরা এক্ষেত্রে সবসময় অ্যারে নোটেশন ইউজ করবো। এবার মূলকথায় ফিরে যায়। আমরা তো এভাবেও কী আর ভ্যালু পাচ্ছি। তাহলে ঐ দুইটা মেথডের কাজ কি? আমরা একটু দেখি। + +```js +const empty = {}; +console.log(empty); // {} +console.log(Boolean(empty)); // true +``` + +আমরা যদি জানতে চাই আমাদের অবজেক্টটা সত্যিই ফাঁকা কিনা তাহলে এভাবে পারবো না। কারণ ফাঁকা অবজেক্ট, ফাঁকা অ্যারে সবসময় true রিটার্ন করবে। সেক্ষেত্রে আমরা `Object.keys()` এর সাহায্য নিবো। + +```js +const empty = {}; +console.log(Object.keys(empty)); // [] +``` + +এখন ফাঁকা অ্যারেও তো true রিটার্ন করবে কারণ ফাঁকা অ্যারেও একটা truthy value. আমাদের অবজেক্ট প্রোপারলি ফাঁকা কিনা তা জানার জন্য আমাদেরকে নিচের কাজটা করতে হবে। + +```js +const empty = {}; +console.log(Object.keys(empty).length === 0); // true +``` + +তার মানে যদি লেংথ ০ হয় তাহলে আমাদের অবজেক্টটা ফাঁকা বলে ধরে নিতে পারি। + +এছাড়াও আছে `Object.entries()` মেথড। এটার কাজটা আমরা দেখি একটু। + +```js +console.log(Object.entries(microphone)); +/* +[ + [ 'brand', 'Fifine' ], + [ 'indictor', true ], + [ 'price', 8000 ], + [ 'color', 'Black' ], + [ 'startRecording', [Function: startRecording] ], + [ 'stopRecording', [Function: stopRecording] ] +] +*/ +``` + +ছিল অবজেক্ট। হয়ে গেলো কী আর ভ্যালু এর জন্য আলাদা আলাদা অ্যারে। এটা আমাদের ভবিষ্যতে অনেক কাজে লাগবে। + +এখন ধরেন আমাদের কাছে একটা অ্যারে আছে। আমরা চাইছি সেটা থেকে অবজেক্ট বানাতে। তা জন্য আমাদের ব্যবহার করতে হবে `fromEntries` মেথডটি। + +```js +const arr = [ + ['brand', 'Fifine'], + ['indictor', true], + ['price', 8000], + ['color', 'Black'], +]; + +console.log(Object.fromEntries(arr)); // { brand: 'Fifine', indictor: true, price: 8000, color: 'Black' } +``` + +### Function vs Method + +যখন একটা ফাংশন একটা অবজেক্টের মধ্যে থাকে তখন আমরা সেটাকে বলি মেথড। তাহলে আমরা যে array.filter(), array.push(), array.map(), array.splice() ব্যবহার করেছি এগুলো সবগুলোই হচ্ছে মেথড। এরা ফাংশন না। ফাংশন আর মেথডের মধ্যে একটাই পার্থক্য। ফাংশন স্বাধীনভাবে যেকোনো জায়গায় কল করা যায় কিন্তু মেথড পারা যায় না। একটা উদাহরণ দিলে আমরা ভালভাবে বুঝতে পারবো। + +```js +const microphone = { + brand: 'Fifine', + indictor: true, + price: 8000, + color: 'Black', + startRecording() { + console.log('recording started'); + }, + stopRecording() { + console.log('recording stopped'); + }, +}; + +function startRecording() { + console.log('recording started'); +} + +startRecording(); + +microphone.startRecording(); +``` + +এখানে `startRecording` ফাংশনটা অবজেক্টের ভিতরেও আছে, আবার বাইরেও আছে। এখন বাইরের ফাংশনকে চাইলে আমরা এমনিই কল করতে পারবো। কিছু ছাড়াই। কিন্তু অবজেক্টের ফাংশনকে যদি কল করতে চাই তাহলে অবশ্যই `microphone.startRecording()` লিখতে হবে। এটাই বেসিক পার্থক্য। তাহলে আমরা কোনোকিছুর পর ডট দিয়ে যাই লিখবো অর্থাৎ অবজেক্টের মধ্যে কোনো ফাংশন থাকলে সেগুলো সবগুলো হলো মেথড। আর ইন্ডিপেন্ডেন্টলি যা লিখবো সেগুলো হচ্ছে ফাংশন। + +## Object as a Data Structure + +আমরা চাইছি কয়েকজন ছাত্রের ইনফরমেশন স্টোর করতে। সেখানে থাকবে একজন ছাত্রের একটা ইউনিক আইডি, তার নাম এবং তার ইমেইল। এখন আমরা প্রথমে একটা ইউনিক আইডি জেনারেট করার ফাংশন তৈরি করে ফেলি। + +```js +function uuidv4() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + const r = (Math.random() * 16) | 0; + const v = c == 'x' ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); +} +``` + +এই ফাংশনটা গুগল থেকে নেয়া। এখন আমরা এই ছাত্রদের ইনফরমেশন অ্যারে দিয়েও স্টোর করতে পারি আবার অবজেক্ট দিয়েও পারি। প্রথমেই বলে রাখি সব কাজের জন্য অ্যারে ভাল না আবার সব কাজের জন্য অবজেক্টও ভাল না। আমাদেরকে আমাদের কাজের উপর ভিত্তি করে সিদ্ধান্ত নিতে হবে কখন আমরা অ্যারে ইউজ করবো আর কখন অবজেক্ট। প্রথমে আমরা একটু অ্যারে নিয়ে কাজ করে দেখি। এরপর অবজেক্ট নিয়ে করবো। + +### Array + +আমাদের সমস্ত ছাত্রের ইনফরমেশন আমরা অ্যারেতে স্টোর করে রাখি এখন। + +```js +const students = [ + { + id: uuidv4(), + name: 'Md Al-Amin', + email: 'alamin@test.com', + }, + { + id: uuidv4(), + name: 'Akib Ahmed', + email: 'akib@test.com', + }, + { + id: uuidv4(), + name: 'Elias Emon', + email: 'elias@test.com', + }, +]; +``` + +যেহেতু আমরা UI নিয়ে কাজ করছি না তাই আমরা চাইবো না বারবার আইডি চেইঞ্জ হোক। আমরা একবার প্রোগ্রাম রান করে যে আউটপুট জেনারেট হবে সেটাকেই স্টোর করে রাখবো। বারবার আইডি চেইঞ্জ হলে আমরা আমাদের যে অপারেশন তা ঠিকভাবে করতে পারবো না। সুতরাং আমরা প্রথমবার রান করার পর সেই আউটপুটকে স্টোর করে নিই আগে। + +```js +const students = [ + { + id: '67de71e5-0eac-474f-ab51-850ba9b31ed5', + name: 'Md Al-Amin', + email: 'alamin@test.com', + }, + { + id: 'ebdf6b78-c32b-4b1d-8574-e8c655b05c1e', + name: 'Akib Ahmed', + email: 'akib@test.com', + }, + { + id: 'ee729e84-a84e-4adf-b32c-4647a7114d5b', + name: 'Elias Emon', + email: 'elias@test.com', + }, +]; +``` + +অ্যারেতে স্টোর করে রাখলে আমরা কিছু সুবিধা পাবো। সেগুলো হলোঃ + +1. Create a new one +2. Update +3. Delete +4. Filter +5. Easily Traverse + +এবার আমরা এক এক করে এই কাজগুলো দেখি। + +- Create a new one + +এটা সবচেয়ে সহজ কাজ। আমরা জানি আমরা যখন অ্যারেতে একটা ডাটা ইনসার্ট করতে চাই দুইটা মেথড আমরা ইউজ করতে পারি। যদি সবার শেষে ইনসার্ট করতে চাই তাহলে `push` মেথড ব্যবহার করবো, আর যদি সবার প্রথমে ইনসার্ট করতে চাই তাহলে `unshift` মেথড ব্যবহার করবো। কিন্তু `unshift` অনেক এক্সপেন্সিভ। কেন এক্সপেন্সিভ? কারণ আমাকে প্রতিটা ইলেমেন্ট এক ঘর করে ডান পাশে শিফট করতে হচ্ছে। যার কারনে অনেক বেশি অপারেশন ঘটাতে হচ্ছে। অর্থাৎ এর কমপ্লেক্সিটি O(n)। অন্যদিকে `push` মেথডে আমার কাউকে সরাতে হচ্ছে না। শুধু শেষে ডাটাটা বসিয়ে দিলেই হলো। অর্থাৎ এর কমপ্লেক্সিটি O(1)। O(n) হলো ডাটা সাইজের উপর এর এক্সিকিউশন টাইম নির্ভর করে। সাইজ ছোট হলে কম সময় আর সাইজ বড় হলে বেশি সময়। এটার সমস্যা হলো আমরা এখানে বিগ অ্যামাউন্টের ডাটা স্টোর করে রাখতে পারবো না। আর O(1) হলো ডাটার সাইজ কতো বড় বা ছোট সেটা বিবেচ্য না। সেটা একটা নির্দিষ্ট সময়েই এক্সিকিউট হবে তা বড় সাইজের ডাটা হোক বা ছোট সাইজের। তার এক্সিকিউশন টাইম কন্সট্যান্ট। এক্ষেত্রে ডাটা ইনসার্টের জন্য আমরা `push` মেথড ব্যবহার করবো। + +```js +students.push({ + id: '0a2c956c-a9f4-48b9-83fa-551b432dfb2b', + name: 'Fahim Faisal', + email: 'fahim@test.com', +}); +``` + +এখন আমাদের প্রোগ্রাম রান করালে দেখা যাবে আমাদের অ্যারেতে নতুন ডাটা ক্রিয়েট হয়ে গেছে। + +- Update + +আমরা দুইভাবে আপডেট করতে পারি। একটা হচ্ছে যাকে আপডেট করতে হবে find মেথডের মাধ্যমে সেই অবজেক্টকে বের করে তাকে আপডেট করা। আরেকটা হলো ঐ অবজেক্টের ইনডেক্সকে findIndex মেথডের মাধ্যমে বের করে সেটা ধরে আপডেট করা। অবজেক্ট ধরে যদি আপডেট করতে চাই সেক্ষেত্রে একটা সমস্যা আছে। সেটা একটু আমরা দেখি। + +```js +const idToUpdate = 'ee729e84-a84e-4adf-b32c-4647a7114d5b'; +const updatedData = { + name: 'Habiba Akhtar', + email: 'habiba@test.com', +}; + +let updatedObj = students.find((item) => item.id === idToUpdate); +updatedObj = { + id: idToUpdate, + ...updatedObj, +}; +console.log('Updated', students); +/* +Updated [ + { + id: '67de71e5-0eac-474f-ab51-850ba9b31ed5', + name: 'Md Al-Amin', + email: 'alamin@test.com' + }, + { + id: 'ebdf6b78-c32b-4b1d-8574-e8c655b05c1e', + name: 'Akib Ahmed', + email: 'akib@test.com' + }, + { + id: 'ee729e84-a84e-4adf-b32c-4647a7114d5b', + name: 'Elias Emon', + email: 'elias@test.com' + }, + { + id: '0a2c956c-a9f4-48b9-83fa-551b432dfb2b', + name: 'Fahim Faisal', + email: 'fahim@test.com' + } +] +*/ +``` + +কিছুই আপডেট হলো না। কারণ আমরা অবজেক্ট অ্যাসাইন করছি। আর যেহেতু অ্যাসাইন করছি সেহেতু এর রেফারেন্সও আলাদা হয়ে গেছে। আলাদা রেফারেন্সের কারণে আমার আপডেট কাজ করছে না। এবার আসি ইনডেক্স বের করে কিভাবে আপডেট করতে পারি সেটা নিয়ে। + +```js +const idToUpdate = 'ee729e84-a84e-4adf-b32c-4647a7114d5b'; +const updatedData = { + name: 'Habiba Akhtar', + email: 'habiba@test.com', +}; + +const updatedIndex = students.findIndex((item) => item.id === idToUpdate); +students[updatedIndex] = { + ...students[updatedIndex], + ...updatedData, +}; +console.log('Updated', students); +``` + +তিনটা ডট দেয়াকে জাভাস্ক্রিপ্টে বলে স্প্রেড অপারেটর। এর মানে হলো অরিজিনাল অবজেক্টে যা যা আছে সবই থাকবে। আর নতুন ডাটা অনুযায়ী সেটা আপডেট হবে। যখন কোনো কিছু রিঅ্যাসাইনের কাজ আসবে তখন আমরা find ব্যবহার না করে findIndex ব্যবহার করবো। এই আপডেট করা মোটামুটি রকমের কমপ্লেক্স। তাই এর কমপ্লেক্সিটি আমরা O(n) হিসেবে ধরতে পারি। + +- Delete + +ডিলিট করাটা তুলনামূলক সহজ। কিন্তু আমরা ডিলিটের জন্য দুইটা মেথড ইউজ করতে পারি `splice` এবং `filter`। এই দুইটা মেথডের কমপ্লেক্সিটি O(n)। এখানে আমরা splice নিয়ে কাজ করছি। পরের ধাপে আমরা filter অপারেশন দেখাবো। আমরা যদি আমাদের upodatedIndex ডিলিট করতে চাই তাহলে এভাবে লিখতে হবে। + +```js +students.splice(updatedIndex, 1); +``` + +- Filter + +```js +const filteredStudents = students.filter((item) => item.id !== idToUpdate); +console.log(filteredStudents); +``` + +- Easily Traverse + +অ্যারের ক্ষেত্রে ট্রাভার্স করা অনেক সহজ। ধরি আমরা ছাত্রদের নাম জানতে চাইছি। তিনভাবে আমরা অ্যারে ট্রাভার্সের মাধ্যমে নাম বের করে আনতে পারি। এগুলো হলো। `for` loop, `for in` loop, `for of` loop। নিচে তিনটারই উদাহরণ দেয়া হলো। + +```js +for (let i = 0; i < students.length; i++) { + console.log(students[i].name); +} + +for (let i in students) { + console.log(students[i].name); +} + +for (let student of students) { + console.log(student.name); +} +``` + +এছাড়াই কিছু বিল্ট-ইন মেথড রয়েছে অ্যারে ট্রাভার্সের জন্য। যেমন `forEach`, `map`, `filter`, `every`, `reduce`, `some`, `find`, `findIndex` ইত্যাদি। তাহলে আমরা বুঝলাম যে অ্যারে অনেক সহজে ট্রাভার্স করা যায়। এটার কমপ্লেক্সিটি O(n)। + +### Object Over Array + +এবার আমরা আমাদের ছাত্রদের অ্যারেকে একটা অবজেক্টে রূপান্তরিত করি এবং একে একে অ্যারের ক্ষেত্রে যে যে অপারেশন করেছি সেই সেই অপারেশন করার চেষ্টা করি। + +```js +const students = { + '67de71e5-0eac-474f-ab51-850ba9b31ed5': { + id: '67de71e5-0eac-474f-ab51-850ba9b31ed5', + name: 'Md Al-Amin', + email: 'alamin@test.com', + }, + 'ebdf6b78-c32b-4b1d-8574-e8c655b05c1e': { + id: 'ebdf6b78-c32b-4b1d-8574-e8c655b05c1e', + name: 'Akib Ahmed', + email: 'akib@test.com', + }, + 'ee729e84-a84e-4adf-b32c-4647a7114d5b': { + id: 'ee729e84-a84e-4adf-b32c-4647a7114d5b', + name: 'Elias Emon', + email: 'elias@test.com', + }, +}; +``` + +আমাদের অবজেক্ট রেডি। এবার আমরা অপারেশনগুলো দেখি এক এক করে। + +- Create a new one + +অ্যারেতে আমরা সহজেই push মেথড ইউজ করে ডাটা ইনসার্ট করেছিলাম। কিন্তু অবজেক্টে তো এরকম কিছু নেই। তাহলে আমরা কিভাবে এই অপারেশন চালাবো। দেখি একটু কিভাবে করা যায়। + +```js +const std = { + id: uuidv4(), + name: 'Feroz Khan', + email: 'feroz@test.com', +}; + +students[std.id] = std; +``` + +একটাই উপায়। এবং সবচেয়ে সহজ উপায়। এই উপায়ে আপনি যতো চান ততো ডাটা ক্রিয়েট করতে পারবেন। খুব সহজ। আর এর কমপ্লেক্সিটি হলো O(1)। + +- Update + +যেহেতু এটা অ্যারে না সেহেতু এখানে find বা findIndex কিছুই কাজ করবে না। তাহলে কিভাবে আপডেট করবো। খুব সহজ। চলুন দেখা যাক। + +```js +const idToBeUpdated = 'ee729e84-a84e-4adf-b32c-4647a7114d5b'; +const updatedData = { + name: 'HM Azizul', + email: 'azizul@test.com', +}; +students[idToBeUpdated] = { + ...students[idToBeUpdated], + ...updatedData, +}; +``` + +এখন যদি আপনি প্রোগ্রাম রান করান দেখবেন আপনার ডাটা আপডেট হয়ে গেছে। কিন্তু যেহেতু এখানে কোনো ধরণের বিল্ট-ইন মেথড লাগেনি তাই এর কমপ্লেক্সিটি হবে O(1)। + +- Delete + +অবজেক্ট থেকে ডিলিট করা খুব সহজ।মানে এত সহজ হওয়া সম্ভব না। জাস্ট একটা কীওয়ার্ড ব্যবহার করলে ডিলিট হয়ে যাবে। + +```js +delete students[idToBeUpdated]; +``` + +কাজ শেষ। কমপ্লেক্সিটি O(1)। + +- Get anything if you have the key + +যদি আমাদের কোনো অবজেক্টের কী জানা থাকে তাহলে ১ সেকেন্ডের মধ্যে আমরা সেই অবজেক্টকে পেয়ে যাবো। কিভাবে> দেখুন তাহলে- + +```js +console.log(students['67de71e5-0eac-474f-ab51-850ba9b31ed5']); +``` + +জাস্ট এটুকুই। আর এটার কমপ্লেক্সিটিও O(1)। + +- Traverse + +আমরা for in লুপ ব্যবহার করে খুব সহজেই অবজেক্ট ট্রাভার্স করতে পারি। যেমন যদি আমরা অবজেক্টে থাকা সবার নাম বের করে আনতে চাই তাহলে কিভাবে করবো? + +```js +for (let key in students) { + console.log(students[key].name); +} +``` + +কিন্তু এটা একটা ইম্পেরেটিভ ওয়ে। আমরা যখন রিয়্যাক্ট নিয়ে কাজ করবো তখন jsx এ কিন্তু for in ব্যবহার করতে পারবো না। আমাদের দরকার একটা ডিক্লারেটিভ ওয়ে। সেটার জন্য আমরা অবজেক্টের আলোচনায় দুইটা মেথডের কথা বলেছিলাম। একটা ছিল `Object.keys()` এবং অন্যটা হলো `Object.values()`। চলুন দেখি এগুলো কিভাবে অ্যাপ্লাই করতে পারি। + +```js +Object.keys(students).forEach((key) => { + const student = students[key]; + console.log(student.name); +}); +``` + +এখানে key না নিয়েও আমরা সরাসরি value নিয়ে কাজ করতে পারতাম। যেমন + +```js +Object.values(students).forEach((student) => { + console.log(student.name); +}); +``` + +এটার মাধ্যমে আমরা অবজেক্ট থেকে অ্যারে বানিয়ে অ্যারের সমস্ত কাজ আমরা করতে পারি। এতে কিন্তু আমাদের কোনো এক্সট্রা মেমোরি লাগছে না। কারণ আমরা এটাকে কোথাও স্টোর করে রাখছি না। এটা তার কাজ শেষ করে গার্বেজ কালেক্ট করে ক্লিয়ার করে ফেলবে। + +তাহলে দেখা যাচ্ছে যে যে কাজ আমরা অ্যারে দিয়ে করতে পারতাম সেগুলো আমরা অবজেক্ট দিয়েও করতে পারি। এবং অনেক ক্ষেত্রে অনেক সহজেই করতে পারি। + +## Comparison of object and array operation costs + +```js +const arr = []; +const arrToObj = {}; +for (let i = 0; i < 5000000; i++) { + const o = { + id: i, + value: i, + }; + arr.push(o); + arrToObj[i] = o; +} + +console.time('array'); +let id = 4999999; +const obj = arr.find((item) => item.id === id); +obj.value = 555; +console.timeEnd('array'); // 104.901ms + +console.time('obj'); +arrToObj[id].value = 999; +console.timeEnd('obj'); // 0.019ms +``` + +অ্যারের অপারেশনে সময় লেগেছে ১০৪.৯০১ মিলিসেকেন্ড আর অবজেক্টের অপারেশনে লেগেছে ০.০১৯ মিলিসেকেন্ড। + +```js +console.time('array'); +arr.unshift({ + id: 5000000, + value: 5000000, +}); +console.timeEnd('array'); // 15.084ms + +console.time('obj'); +arrToObj[5000000] = { + id: 5000000, + value: 5000000, +}; +console.timeEnd('obj'); // 0.018ms +``` + +অ্যারের জন্য লেগেছে ১৫.০৮৪ মিলিসেকেন্ড আর অবজেক্টের ক্ষেত্রে লেগেছে ০.০১৮ মিলিসেকেন্ড। + +```js +console.time('array'); +const index = arr.findIndex((item) => item.id === 4000000); +arr.splice(index, 1); +console.timeEnd('array'); // 93.135ms + +console.time('obj'); +delete arrToObj[4000000]; +console.timeEnd('obj'); // 0.015ms +``` + +অ্যারের ক্ষেত্রে লেগেছে ৯৩.১৩৫ মিলিসেকেন্ড আর অবজেক্টের ক্ষেত্রে লেগেছে ০.০১৫ মিলিসেকেন্ড। + +সবক্ষেত্রে দেখা যাচ্ছে তাহলে অবজেক্ট বিজয়ী। তবে কিছু কিছু ক্ষেত্রে অ্যারে নিয়ে কাজ করা লাগে। যেমন যখন আমার অর্ডারড ডাটা লাগবে, অর্থাৎ সিকোয়েন্স মেইনটেইন করতে হবে তখন অ্যারে মাস্ট। + +## Resource for this lecture + +এই লেকচারের সমস্ত রিসোর্স [লেকচার ৫](../06.JavaScript%20Array%20and%20Object%20Deep%20Dive/resource.md) এবং [লেকচার ৬](./resource.md) এ পাবেন। + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/docs/Lectures/Fundamentals/06/Screenshot_1.png b/docs/Lectures/Fundamentals/06/Screenshot_1.png new file mode 100644 index 0000000..7b87186 Binary files /dev/null and b/docs/Lectures/Fundamentals/06/Screenshot_1.png differ diff --git a/docs/Lectures/Fundamentals/06/Source Code/Array Traverse/SourceCode.md b/docs/Lectures/Fundamentals/06/Source Code/Array Traverse/SourceCode.md new file mode 100644 index 0000000..7c12ba2 --- /dev/null +++ b/docs/Lectures/Fundamentals/06/Source Code/Array Traverse/SourceCode.md @@ -0,0 +1,72 @@ +
+ declarativeWay.js +

This is Source Code Of object.js

+ +```javascript +const numbers = [1, 2, 3, 4, 5]; + +// sum of array elements +sum = 0; +numbers.forEach((element) => { + sum += element; +}); +console.log(sum); + + +const cb = () => { + console.log('Hello') +} +numbers.forEach(cb) +// Hello +// Hello +// Hello +// Hello +// Hello + + +numbers.forEach(function (value, index, arr) { + // console.log(arguments) + console.log(value, index, arr) +}) +// [Arguments] { '0': 1, '1': 0, '2': [ 1, 2, 3, 4, 5 ] } +// [Arguments] { '0': 2, '1': 1, '2': [ 1, 2, 3, 4, 5 ] } +// [Arguments] { '0': 3, '1': 2, '2': [ 1, 2, 3, 4, 5 ] } +// [Arguments] { '0': 4, '1': 3, '2': [ 1, 2, 3, 4, 5 ] } +// [Arguments] { '0': 5, '1': 4, '2': [ 1, 2, 3, 4, 5 ] } + + +numbers.forEach(function (value, _, __) { + if(value % 2 === 0){ + console.log(value) + } +}) + +sum = 0; +numbers.forEach(function (v) { + sum += v +}) + +console.log(sum) + + +``` + +
+ + +
+ imperativeWay.js +

This is Source Code Of object.js

+ +```javascript + +const numbers = [1, 2, 3, 4, 5]; + +let sum = 0; +for (let i = 0; i < numbers.length; i++) { + sum += numbers[i]; +} + +``` + +
\ No newline at end of file diff --git a/docs/Lectures/Fundamentals/06/Source Code/Array Traverse/declarativeWay.js b/docs/Lectures/Fundamentals/06/Source Code/Array Traverse/declarativeWay.js new file mode 100644 index 0000000..fdeaad8 --- /dev/null +++ b/docs/Lectures/Fundamentals/06/Source Code/Array Traverse/declarativeWay.js @@ -0,0 +1,44 @@ +const numbers = [1, 2, 3, 4, 5]; + +// sum of array elements +sum = 0; +numbers.forEach((element) => { + sum += element; +}); +console.log(sum); + + +const cb = () => { + console.log('Hello') +} +numbers.forEach(cb) +// Hello +// Hello +// Hello +// Hello +// Hello + + +numbers.forEach(function (value, index, arr) { + // console.log(arguments) + console.log(value, index, arr) +}) +// [Arguments] { '0': 1, '1': 0, '2': [ 1, 2, 3, 4, 5 ] } +// [Arguments] { '0': 2, '1': 1, '2': [ 1, 2, 3, 4, 5 ] } +// [Arguments] { '0': 3, '1': 2, '2': [ 1, 2, 3, 4, 5 ] } +// [Arguments] { '0': 4, '1': 3, '2': [ 1, 2, 3, 4, 5 ] } +// [Arguments] { '0': 5, '1': 4, '2': [ 1, 2, 3, 4, 5 ] } + + +numbers.forEach(function (value, _, __) { + if(value % 2 === 0){ + console.log(value) + } +}) + +sum = 0; +numbers.forEach(function (v) { + sum += v +}) + +console.log(sum) diff --git a/docs/Lectures/Fundamentals/06/Source Code/Array Traverse/imperativeWay.js b/docs/Lectures/Fundamentals/06/Source Code/Array Traverse/imperativeWay.js new file mode 100644 index 0000000..7af39d5 --- /dev/null +++ b/docs/Lectures/Fundamentals/06/Source Code/Array Traverse/imperativeWay.js @@ -0,0 +1,6 @@ +const numbers = [1, 2, 3, 4, 5]; + +let sum = 0; +for (let i = 0; i < numbers.length; i++) { + sum += numbers[i]; +} diff --git a/docs/Lectures/Fundamentals/06/Source Code/SourceCode.md b/docs/Lectures/Fundamentals/06/Source Code/SourceCode.md new file mode 100644 index 0000000..a5a969e --- /dev/null +++ b/docs/Lectures/Fundamentals/06/Source Code/SourceCode.md @@ -0,0 +1,292 @@ +
+ arr.js +

This is Source Code Of arr.js

+ + +```javascript +const arr = [1, 2, 3, null, false, 4, 5, "", "test", 6, 7]; + +let count = 0; +for (let i = 0; i < arr.length; i++) { + for (let j = i; j < arr.length - 1; j++) { + if (!arr[j] || typeof arr[j] !== "number") { + arr[j] = arr[j + 1]; + arr[j + 1] = undefined; + } + } + if (arr[i] === undefined) { + count++; + } +} +arr.length -= count; +console.log(arr); + +// explanation +// arr = [1, false, true, '', 2, 3] +// i = 1, j = 3 +// 1, true, '', 2, 3, undefined +// i = true, j = 3 +// 1, '', 2, 3, undefined, undefined +// i = '', j = 3 +// 1, 2, 3, undefined, undefined, undefined + +// [1, 2, 3] + + +// shortcut +const filteredArray = arr.filter((v) => typeof v === 'number') + +console.log(filteredArray); + + +// using new array +const newArr = [] +for(let i = 0; i < arr.length; i++){ + if(typeof arr[i] === 'number'){ + newArr.push(arr[i]) + } +} +console.log(newArr); + + +// calculate fibonnacci number +function fib(n){ + if(n == 0 || n == 1) return n; + return fib(n - 1) + fib(n - 2) +} + +console.log(fib(10)); +``` + +
+ + + + + +
+ delete.js +

This is Source Code Of delete.js

+ + +```javascript + + +const arr = [ + { + id: 1, + value: 10, + }, + { + id: 2, + value: 20, + }, + { + id: 3, + value: 30, + }, + { + id: 4, + value: 40, + }, + { + id: 5, + value: 50, + }, +]; + + +// splice ==> mutable +// const index = arr.findIndex(item => { +// item.id === 4 +// }) +// const arr1 = arr.splice(index, 1) +// console.log(arr); + + +// filter ==> immutable +const arr2 = arr.filter(item => { + return item.id !== 4 +}) +console.log(arr); +console.log(arr2); + +``` + +
+ + + + +
+ object.js +

This is Source Code Of object.js

+ +```javascript + +// object literal +const microphone = { + brand: 'Fantech', + indicator: true, + price: 3400, + color: 'white', + + // methods + startRecording() { + console.log('Recording started'); + }, + stopRecording() { + console.log('Recording stopped'); + } +} + +/** + * There are two different parts in object + * 1. Noun / Adjective (State/data/property/field) + * 2. Verb / (functionalities -> start, stop) + */ + +microphone.startRecording() +microphone.stopRecording() +console.log(microphone); +console.log(Object); + + +// constructor function +const testObj = new Object() +testObj.name = 'Test Object' +testObj.time = new Date() +console.log(testObj); +console.log(testObj.time.getDate()); + +// object k freeze kore dey +// new property add korte dey na +Object.freeze(microphone) +microphone.newProperty = 'hi' +console.log(microphone); + + + +// get key and value +console.log(Object.keys(microphone)); +console.log(Object.values(microphone)); +// [ +// 'brand', +// 'indicator', +// 'price', +// 'color', +// 'startRecording', +// 'stopRecording' +// ] +// [ +// 'Fantech', +// true, +// 3400, +// 'white', +// [Function: startRecording], +// [Function: stopRecording] +// ] + + +// concat function +console.log('micro'.concat('phone')); +console.log('micro' + 'phone'); + + + +// notation +// dot notation -> microphone.brand +// array notation -> microphone[k] +for(let k in microphone){ + console.log(k, microphone[k]); +} +// brand Fantech +// indicator true +// price 3400 +// color white +// startRecording [Function: startRecording] +// stopRecording [Function: stopRecording] + + + +// check is a object is empty or not +const empty ={} +if(Object.keys(empty).length === 0){ + console.log('This object is empty'); +} + + +// object to key value pair +console.log(Object.entries(microphone)); +const array = [ + [ 'brand', 'Fantech' ], + [ 'indicator', true ], + [ 'price', 3400 ], + [ 'color', 'white' ] +] +console.log(Object.fromEntries(array)); +// { brand: 'Fantech', indicator: true, price: 3400, color: 'white' } + +``` + +
+ + + +
+ update.js +

This is Source Code Of update.js

+ +```javascript +const arr = [ + { + id: 1, + value: 10, + }, + { + id: 2, + value: 20, + }, + { + id: 3, + value: 30, + }, + { + id: 4, + value: 40, + }, + { + id: 5, + value: 50, + }, +]; + +// arr.findIndex ==> not mutable +// const index = arr.findIndex(v => { +// return v.id === 4; +// }) + +// console.log(index); +// console.log(arr); +// arr[index].value = 100 +// console.log(arr); + + + +// arr.find ==> mutable +const obj = arr.find((v) => { + return v.id === 4; +}); +obj.value = 100; +console.log(obj); +console.log(arr); + + + +const obj2 = arr[2] +obj2.value = 300 +console.log(obj); +console.log(arr); +``` + +
diff --git a/docs/Lectures/Fundamentals/06/Source Code/arr.js b/docs/Lectures/Fundamentals/06/Source Code/arr.js new file mode 100644 index 0000000..9292659 --- /dev/null +++ b/docs/Lectures/Fundamentals/06/Source Code/arr.js @@ -0,0 +1,52 @@ +const arr = [1, 2, 3, null, false, 4, 5, "", "test", 6, 7]; + +let count = 0; +for (let i = 0; i < arr.length; i++) { + for (let j = i; j < arr.length - 1; j++) { + if (!arr[j] || typeof arr[j] !== "number") { + arr[j] = arr[j + 1]; + arr[j + 1] = undefined; + } + } + if (arr[i] === undefined) { + count++; + } +} +arr.length -= count; +console.log(arr); + +// explanation +// arr = [1, false, true, '', 2, 3] +// i = 1, j = 3 +// 1, true, '', 2, 3, undefined +// i = true, j = 3 +// 1, '', 2, 3, undefined, undefined +// i = '', j = 3 +// 1, 2, 3, undefined, undefined, undefined + +// [1, 2, 3] + + +// shortcut +const filteredArray = arr.filter((v) => typeof v === 'number') + +console.log(filteredArray); + + +// using new array +const newArr = [] +for(let i = 0; i < arr.length; i++){ + if(typeof arr[i] === 'number'){ + newArr.push(arr[i]) + } +} +console.log(newArr); + + +// calculate fibonnacci number +function fib(n){ + if(n == 0 || n == 1) return n; + return fib(n - 1) + fib(n - 2) +} + +console.log(fib(10)); \ No newline at end of file diff --git a/docs/Lectures/Fundamentals/06/Source Code/delete.js b/docs/Lectures/Fundamentals/06/Source Code/delete.js new file mode 100644 index 0000000..cddd561 --- /dev/null +++ b/docs/Lectures/Fundamentals/06/Source Code/delete.js @@ -0,0 +1,38 @@ +const arr = [ + { + id: 1, + value: 10, + }, + { + id: 2, + value: 20, + }, + { + id: 3, + value: 30, + }, + { + id: 4, + value: 40, + }, + { + id: 5, + value: 50, + }, +]; + + +// splice ==> mutable +// const index = arr.findIndex(item => { +// item.id === 4 +// }) +// const arr1 = arr.splice(index, 1) +// console.log(arr); + + +// filter ==> immutable +const arr2 = arr.filter(item => { + return item.id !== 4 +}) +console.log(arr); +console.log(arr2); \ No newline at end of file diff --git a/docs/Lectures/Fundamentals/06/Source Code/object.js b/docs/Lectures/Fundamentals/06/Source Code/object.js new file mode 100644 index 0000000..54af386 --- /dev/null +++ b/docs/Lectures/Fundamentals/06/Source Code/object.js @@ -0,0 +1,102 @@ +// object literal +const microphone = { + brand: 'Fantech', + indicator: true, + price: 3400, + color: 'white', + + // methods + startRecording() { + console.log('Recording started'); + }, + stopRecording() { + console.log('Recording stopped'); + } +} + +/** + * There are two different parts in object + * 1. Noun / Adjective (State/data/property/field) + * 2. Verb / (functionalities -> start, stop) + */ + +microphone.startRecording() +microphone.stopRecording() +console.log(microphone); +console.log(Object); + + +// constructor function +const testObj = new Object() +testObj.name = 'Test Object' +testObj.time = new Date() +console.log(testObj); +console.log(testObj.time.getDate()); + +// object k freeze kore dey +// new property add korte dey na +Object.freeze(microphone) +microphone.newProperty = 'hi' +console.log(microphone); + + + +// get key and value +console.log(Object.keys(microphone)); +console.log(Object.values(microphone)); +// [ +// 'brand', +// 'indicator', +// 'price', +// 'color', +// 'startRecording', +// 'stopRecording' +// ] +// [ +// 'Fantech', +// true, +// 3400, +// 'white', +// [Function: startRecording], +// [Function: stopRecording] +// ] + + +// concat function +console.log('micro'.concat('phone')); +console.log('micro' + 'phone'); + + + +// notation +// dot notation -> microphone.brand +// array notation -> microphone[k] +for(let k in microphone){ + console.log(k, microphone[k]); +} +// brand Fantech +// indicator true +// price 3400 +// color white +// startRecording [Function: startRecording] +// stopRecording [Function: stopRecording] + + + +// check is a object is empty or not +const empty ={} +if(Object.keys(empty).length === 0){ + console.log('This object is empty'); +} + + +// object to key value pair +console.log(Object.entries(microphone)); +const array = [ + [ 'brand', 'Fantech' ], + [ 'indicator', true ], + [ 'price', 3400 ], + [ 'color', 'white' ] +] +console.log(Object.fromEntries(array)); +// { brand: 'Fantech', indicator: true, price: 3400, color: 'white' } diff --git a/docs/Lectures/Fundamentals/06/Source Code/task.md b/docs/Lectures/Fundamentals/06/Source Code/task.md new file mode 100644 index 0000000..4e1ddca --- /dev/null +++ b/docs/Lectures/Fundamentals/06/Source Code/task.md @@ -0,0 +1 @@ +- [Make Fun Of Javascript Array](https://youtube.com/playlist?list=PL_XxuZqN0xVDr08QgQHljCecWtA4jBLnS) \ No newline at end of file diff --git a/docs/Lectures/Fundamentals/06/Source Code/update.js b/docs/Lectures/Fundamentals/06/Source Code/update.js new file mode 100644 index 0000000..1fa577d --- /dev/null +++ b/docs/Lectures/Fundamentals/06/Source Code/update.js @@ -0,0 +1,49 @@ +const arr = [ + { + id: 1, + value: 10, + }, + { + id: 2, + value: 20, + }, + { + id: 3, + value: 30, + }, + { + id: 4, + value: 40, + }, + { + id: 5, + value: 50, + }, +]; + +// arr.findIndex ==> not mutable +// const index = arr.findIndex(v => { +// return v.id === 4; +// }) + +// console.log(index); +// console.log(arr); +// arr[index].value = 100 +// console.log(arr); + + + +// arr.find ==> mutable +const obj = arr.find((v) => { + return v.id === 4; +}); +obj.value = 100; +console.log(obj); +console.log(arr); + + + +const obj2 = arr[2] +obj2.value = 300 +console.log(obj); +console.log(arr); \ No newline at end of file diff --git a/docs/Lectures/Fundamentals/06/resource.md b/docs/Lectures/Fundamentals/06/resource.md new file mode 100644 index 0000000..2601acd --- /dev/null +++ b/docs/Lectures/Fundamentals/06/resource.md @@ -0,0 +1,431 @@ +# Resource +## Lecture 6 - JavaScript Array and Object Deep Dive + +## Today’s Agenda + +- Array Operations + + - Map + + ```jsx + const numbers = [1, 2, 3, 4]; + const strs = numbers.map((v) => v.toString()); + console.log(strs); + ``` + + - Filter + + ```jsx + const numbers = [1, 2, 3, 4, false, '', NaN, 5, 6]; + const filteredArr = numbers.filter((v) => v); + console.log(filteredArr); + ``` + + - Reduce + + ```jsx + const numbers = [1, 2, 3, 4, 5, 6]; + const sum = numbers.reduce((a, b) => a + b); + console.log(sum); + + /** + * Map -> [same length as the original array] + * Filter -> [with filtered item] + * Reduce -> No one knows.(Only you know) all possible value + */ + + // we want this -> '1234falseNaN56' + const result = numbers.reduce((acc, cur, i) => { + if (i === 0) { + acc += '['; + } + if (cur) { + acc += cur.toString() + (i < numbers.length - 1 ? ', ' : ''); + } + if (i === numbers.length - 1) { + acc += ']'; + } + return acc; + }, ''); + console.log(result); + + // const result = numbers.reduce((acc, cur) => { + // if (cur) { + // acc.push(cur.toString()); + // } + // return acc; + // }, []); + // console.log(result); + ``` + + ```jsx + const axios = require('axios').default; + const url = 'https://jsonplaceholder.typicode.com/posts'; + + async function getData() { + const { data } = await axios.get(url); + // const result = data.slice(0, 10).map((item) => { + // return { + // userId: item.userId, + // id: item.id, + // title: item.title, + // }; + // }); + const result = data.slice(0, 10).reduce((acc, cur) => { + acc[cur.id] = { + ...cur, + }; + delete acc[cur.id].body; + return acc; + }, {}); + return result; + } + + getData() + .then((data) => console.log(data)) + .catch((e) => console.log(e)); + ``` + + ```jsx + const names = [ + 'Ayman', + 'Abu Rayhan', + 'Anik', + 'Elias Emon', + 'Engr. Sabbir', + 'Fahim Faisal', + 'Feroz Khan', + 'Habib', + 'HM Azizul', + 'Hridoy Saha', + 'Jahid Hassan', + 'Johir', + 'Md Al-Amin', + 'Md Arafatul', + 'Md Ashraful', + 'Parvez', + ]; + + // const namesGroup = { + // A: ['Ayman', 'Abu Rayhan', 'Anik'], + // E: ['Elias Emon', 'Engr. Sabbir'], + // F: ['Fahim Faisal', 'Feroz Khan'], + // }; + + const namesGrouped = names.reduce((acc, cur) => { + const firstLetter = cur[0].toUpperCase(); + if (firstLetter in acc) { + acc[firstLetter].push(cur); + } else { + acc[firstLetter] = [cur]; + } + return acc; + }, {}); + + Object.keys(namesGrouped).forEach((groupKey) => { + console.log('-----------', groupKey, '-----------'); + namesGrouped[groupKey].forEach((name) => console.log(name)); + console.log(); + }); + ``` + +- Object as a Data Structure + + - Array Operations + + ```jsx + /** + * Store 10 students information + * - name + * - email + * - id + */ + + /** + * + * a utility to create a random id + */ + + function uuidv4() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + const r = (Math.random() * 16) | 0; + const v = c == 'x' ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); + } + + const students = [ + { + id: '67de71e5-0eac-474f-ab51-850ba9b31ed5', + name: 'Md Al-Amin', + email: 'alamin@test.com', + }, + { + id: 'ebdf6b78-c32b-4b1d-8574-e8c655b05c1e', + name: 'Akib Ahmed', + email: 'akib@test.com', + }, + { + id: 'ee729e84-a84e-4adf-b32c-4647a7114d5b', + name: 'Elias Emon', + email: 'elias@test.com', + }, + ]; + + /** + * 1. Easily Traverse + * 2. Filter + * 3. Delete (medium) [splice -> O(n), filter -> O(n)] + * 4. Update (medium) (easy) [push -> O(n)] + * 5. Create a new one (easy task) [push -> O(1), unshift -> O(n)] + */ + + // create a new students + students.push({ + id: uuidv4(), + name: 'Fahim Faisal', + email: 'fahim@test.com', + }); + + // update + const idToUpdate = 'ee729e84-a84e-4adf-b32c-4647a7114d5b'; + const updatedData = { + name: 'Habiba Akhtar', + // email: 'habiba@test.com', + }; + + const updatedIndex = students.findIndex((item) => item.id === idToUpdate); + students[updatedIndex] = { + ...students[updatedIndex], + ...updatedData, + }; + console.log('Updated', students); + + // Delete + students.splice(updatedIndex, 1); + + console.log('Deleted', students); + + // forEach, map, filter, every, reduce, some, find, findIndex -> traversing method + + for (let i = 0; i < students.length; i++) { + console.log(students[i].name); + } + + for (let i in students) { + console.log(students[i].name); + } + + for (let student of students) { + console.log(student.name); + } + ``` + + - Object Over Array + + ```jsx + /** + * Store 10 students information + * - name + * - email + * - id + */ + + /** + * + * a utility to create a random id + */ + + function uuidv4() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + const r = (Math.random() * 16) | 0; + const v = c == 'x' ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); + } + + const students = { + '67de71e5-0eac-474f-ab51-850ba9b31ed5': { + id: '67de71e5-0eac-474f-ab51-850ba9b31ed5', + name: 'Md Al-Amin', + email: 'alamin@test.com', + }, + 'ebdf6b78-c32b-4b1d-8574-e8c655b05c1e': { + id: 'ebdf6b78-c32b-4b1d-8574-e8c655b05c1e', + name: 'Akib Ahmed', + email: 'akib@test.com', + }, + 'ee729e84-a84e-4adf-b32c-4647a7114d5b': { + id: 'ee729e84-a84e-4adf-b32c-4647a7114d5b', + name: 'Elias Emon', + email: 'elias@test.com', + }, + }; + + /** + * 1. Easily Traverse [O(n)] + * 1.1 Get anything if you have the key [O(1)] + * 2. Filter + * 3. Delete (medium) [O(1)] + * 4. Update (medium) [O(1)] + * 5. Create a new one (easy task) [O(1)] + */ + + // create + const std = { + id: uuidv4(), + name: 'Feroz Khan', + email: 'feroz@test.com', + }; + + students[std.id] = std; + + // update + const idToBeUpdated = 'ee729e84-a84e-4adf-b32c-4647a7114d5b'; + const updatedData = { + name: 'HM Azizul', + email: 'azizul@test.com', + }; + students[idToBeUpdated] = { + ...students[idToBeUpdated], + ...updatedData, + }; + + // delete + // delete students[idToBeUpdated]; + + // Get + // console.log(students['67de71e5-0eac-474f-ab51-850ba9b31ed5']); + + // Traverse + + // for (let key in students) { + // console.log(students[key]); + // } + + Object.values(students).forEach((student) => { + console.log(student.name, student.email); + }); + ``` + +### Which is performance enhancer? Object or Array? + +```jsx +const arr = []; +const arrToObj = {}; +for (let i = 0; i < 5000000; i++) { + const o = { + id: i, + value: i, + }; + arr.push(o); + arrToObj[i] = o; +} + +console.time('array'); +let id = 4999999; +const obj = arr.find((item) => item.id === id); +obj.value = 555; +console.timeEnd('array'); // 104.901ms + +console.time('obj'); +arrToObj[id].value = 999; +console.timeEnd('obj'); // 0.019ms + +console.time('array'); +arr.unshift({ + id: 5000000, + value: 5000000, +}); +console.timeEnd('array'); // 15.084ms + +console.time('obj'); +arrToObj[5000000] = { + id: 5000000, + value: 5000000, +}; +console.timeEnd('obj'); // 0.018ms + +console.time('array'); +const index = arr.findIndex((item) => item.id === 4000000); +arr.splice(index, 1); +console.timeEnd('array'); // 93.135ms + +console.time('obj'); +delete arrToObj[4000000]; +console.timeEnd('obj'); // 0.015ms +``` + +### Check Truthy value + +```js +let arr = [1, 2, 3, null, undefined, 0, NaN, 4, 5]; +let truthy = arr.filter((v) => v); +// As a default, v will return truthy values. +console.log(truthy); // [1,2,3,4,5] + +// However, if we simply need truthful values and don't want to utilize the default method, we can just put (!! double exclamation) before the array property, It will return only truthy values as well. + +let arr = [1, 2, 3, null, undefined, 0, NaN, 4, 5]; +let truthy = arr.filter((v) => !!v); +console.log(truthy); // [1,2,3,4,5] +``` + +### Performance check for map, filter, reduce + +```jsx +const arr = []; +for (let i = 1; i < 5000000; i++) { + arr.push(i); +} + +console.time('not-optimized'); +arr.filter((item) => item % 2 === 0).map((item) => item * 2); +console.timeEnd('not-optimized'); // 562.423ms + +console.time('optimized'); +arr.reduce((acc, cur) => { + if (cur % 2 === 0) { + acc.push(cur * 2); + } + return acc; +}, []); +console.timeEnd('optimized'); // 238.3ms +``` + +### Implementation of reduce function + +```jsx +function myReduce(arr, cb, init) { + let acc = init; + for (let i = 0; i < arr.length; i++) { + acc = cb(acc, arr[i], i, arr); + } + return acc; +} + +const sum = myReduce([1, 2, 3, 4], (a, b) => a + b, 0); +console.log(sum); + +const arr = [1, 2, '', false, 3, NaN, false, 4, 5, NaN, 6]; +const result = myReduce( + arr, + (acc, cur) => { + if (cur) { + acc.push(cur ** 2); + } + return acc; + }, + [] +); +console.log(result); +``` + +### Important Links + +- [JSONPlaceholder](https://jsonplaceholder.typicode.com/) +- [Class Overview](../../class-overview/Lecture-05-06/README.md) + +### Task + +- How to sort data from object? diff --git a/docs/Lectures/Fundamentals/07/Overview.md b/docs/Lectures/Fundamentals/07/Overview.md new file mode 100644 index 0000000..45f78e6 --- /dev/null +++ b/docs/Lectures/Fundamentals/07/Overview.md @@ -0,0 +1,3 @@ +## QNA 1 - Don't Miss The Last Part + +এই লেকচারে কিছু QNA সেশন ছিল। আর কিছু বইয়ের নাম বলা হয়েছে। সেগুলো সব আপনারা এই [লিংক](./resource.md) এ পাবেন। আপনারা ভিডিও দেখে নিজের প্রয়োজনমতো রিসোর্স নিজেরা তৈরি করে নিবেন। diff --git a/resources/lecture-07/images/computer-science-distilled.jpg b/docs/Lectures/Fundamentals/07/images/computer-science-distilled.jpg similarity index 100% rename from resources/lecture-07/images/computer-science-distilled.jpg rename to docs/Lectures/Fundamentals/07/images/computer-science-distilled.jpg diff --git a/resources/lecture-07/images/elements-of-programming-interviews.jpg b/docs/Lectures/Fundamentals/07/images/elements-of-programming-interviews.jpg similarity index 100% rename from resources/lecture-07/images/elements-of-programming-interviews.jpg rename to docs/Lectures/Fundamentals/07/images/elements-of-programming-interviews.jpg diff --git a/resources/lecture-07/images/head-first-js-programming.jpg b/docs/Lectures/Fundamentals/07/images/head-first-js-programming.jpg similarity index 100% rename from resources/lecture-07/images/head-first-js-programming.jpg rename to docs/Lectures/Fundamentals/07/images/head-first-js-programming.jpg diff --git a/resources/lecture-07/images/introduction-to-algorithms.jpg b/docs/Lectures/Fundamentals/07/images/introduction-to-algorithms.jpg similarity index 100% rename from resources/lecture-07/images/introduction-to-algorithms.jpg rename to docs/Lectures/Fundamentals/07/images/introduction-to-algorithms.jpg diff --git a/resources/lecture-07/images/js-cookbook.jpg b/docs/Lectures/Fundamentals/07/images/js-cookbook.jpg similarity index 100% rename from resources/lecture-07/images/js-cookbook.jpg rename to docs/Lectures/Fundamentals/07/images/js-cookbook.jpg diff --git a/resources/lecture-07/images/js-the-definitive-guide.jpg b/docs/Lectures/Fundamentals/07/images/js-the-definitive-guide.jpg similarity index 100% rename from resources/lecture-07/images/js-the-definitive-guide.jpg rename to docs/Lectures/Fundamentals/07/images/js-the-definitive-guide.jpg diff --git a/resources/lecture-07/images/js-the-good-parts.jpg b/docs/Lectures/Fundamentals/07/images/js-the-good-parts.jpg similarity index 100% rename from resources/lecture-07/images/js-the-good-parts.jpg rename to docs/Lectures/Fundamentals/07/images/js-the-good-parts.jpg diff --git a/resources/lecture-07/images/nodejs-8-the-right-way.jpg b/docs/Lectures/Fundamentals/07/images/nodejs-8-the-right-way.jpg similarity index 100% rename from resources/lecture-07/images/nodejs-8-the-right-way.jpg rename to docs/Lectures/Fundamentals/07/images/nodejs-8-the-right-way.jpg diff --git a/resources/lecture-07/images/you-dont-know-js.jpg b/docs/Lectures/Fundamentals/07/images/you-dont-know-js.jpg similarity index 100% rename from resources/lecture-07/images/you-dont-know-js.jpg rename to docs/Lectures/Fundamentals/07/images/you-dont-know-js.jpg diff --git a/resources/lecture-07/README.md b/docs/Lectures/Fundamentals/07/resource.md similarity index 100% rename from resources/lecture-07/README.md rename to docs/Lectures/Fundamentals/07/resource.md diff --git a/docs/Lectures/Fundamentals/08/Overview.md b/docs/Lectures/Fundamentals/08/Overview.md new file mode 100644 index 0000000..fee19e1 --- /dev/null +++ b/docs/Lectures/Fundamentals/08/Overview.md @@ -0,0 +1,765 @@ +## Lecture 8 - Understand JavaScript Functions | Function as a value + +আজ আমাদের আলোচ্য বিষয় হলো ফাংশন। জাভাস্ক্রিপ্টে আমরা যতটা না অবজেক্ট অরিয়েন্টেড প্রোগ্রামিং নিয়ে কাজ করবো তার চেয়ে বেশি কাজ করবো ফাংশনাল প্রোগ্রামিং নিয়ে। বিভিন্ন প্রোগ্রামিং ল্যাঙ্গুয়েজে এখন ফাংশনাল প্রোগ্রামিং নিয়ে কাজ হচ্ছে। প্রতিটা ল্যাঙ্গুয়েজ এখন ফাংশনাল প্রোগ্রামিং এর গুরুত্বটা বুঝেছে। তবে সবার আগে আমাদের জানতে হবে এর গুরুত্ব। ফাংশনাল প্রোগ্রামিং আসলে কি? ফাংশনাল প্রোগ্রামিং কেন গুরুত্বপূর্ন? যদি এটা আমরা বুঝতে পারি, তবে অনেক বড় একটা অধ্যায় আমাদের সামনে উন্মুক্ত হবে। তবে এতসব বোঝার আগে আমাদের বুঝতে হবে ফাংশন কি? কারণ আমরা অনেকেই ফাংশনের অতো গভীরে গিয়ে বুঝি না। আর বুঝলেও জাভাস্ক্রিপ্টের মতো করে বুঝি না। + +জাভাস্ক্রিপ্ট মোটেও একটা ফাংশনাল প্রোগ্রামিং ল্যাঙ্গুয়েজ নয়। জাভাস্ক্রিপ্টে ফাংশনাল প্রোগ্রামিং করা গেলেও এটা একটা ইমপিওর ফাংশনাল প্রোগ্রামিং। পিওর এবং ইমপিওর ফাংশনাল প্রোগ্রামিং ল্যাঙ্গুয়েজ নিয়ে ইতোপূর্বে লেকচার ২ এ আলোচনা করা হয়েছে। আপনারা যারা ক্লিয়ার না তারা এই [লেকচার](../Fundamentals/02.We%20Need%20Freedom,%20We%20have%20to%20Stop%20Technology%20War/We%20Need%20Freedom.md) দেখে আসতে পারেন। + + +## Function + +ফাংশন হচ্ছে একটা মেশিন। আমরা একটা ফাইল ক্রিয়েট করে যে যে কোড করে একটা কাজ করতে পারি, সেই কাজ আমরা ফাংশনের মাধ্যমেও করতে পারি। ফাংশন আমরা মূলত একটা কাজ রিপিট করার উদ্দেশ্যে ব্যবহার করা হয়। যদি রিপিটের উদ্দেশ্যেই ব্যবহার করা হয় তাহলে লুপ কেন নয়? লুপও তো আমরা রিপিট করার জন্যই ব্যবহার করি। আমরা ফাংশন ব্যবহার করি কারণ ফাংশন আমাদের কন্ট্রোল দেয়। আমরা চাইলে যেকোনো জায়গায় আমাদের প্রয়োজনমতো লুপ ব্যবহার করতে পারি না। কারণ এক জায়গায় লুপ ব্যবহার করলে সে চলতেই থাকবে। লুপের ক্ষেত্রে আমরা শুধু কখন শুরু করতে হবে, কখন থামতে হবে আর কখন স্কিপ করে যেতে হবে তা কন্ট্রোল করতে পারি। কিন্তু ফাংশনের বেলায় আমরা যেখানে যত খুশি যেভাবে খুশি সেভাবেই ব্যবহার করতে পারি। + +আমরা বিগিনাররা যখন প্রব্লেম সলভ করতে যাই তখন বুঝতে পারি না কখন ফাংশন নিতে হবে। আমরা দেখা যায় একটা ফাংশনের মধ্যে সব লিখে বসে থাকি। আমরা ফাংশনকে মেশিন বলে মনে না করে রোবট মনে করি। এক একটা ফাংশন এক একটা হেলপার রোবট যারা ছোট ছোট কাজ করার জন্য তৈরি হয়েছে। তাহলে যেখানেই কাজ করার প্রশ্ন উঠবে সেখানেই ফাংশন লিখতে হবে। যেমন আমরা আমাদের প্রাত্যহিক রুটিনটা একটু ভাবি। যেমন আমরা সকালে ঘুম থেকে উঠি, এরপর ওয়াশরুমে গিয়ে ফ্রেশ হই, ব্রেকফাস্ট করি, স্কুল / কলেজ / অফিসে যায়, ওখান থেকে ফিরি, ডিনার করি, শেষে ঘুমাই। একটু যদি আমরা পয়েন্ট ধরে ধরে দেখি তাহলে এমন দেখাবে। + +```txt +== Daily Routine == +awake from sleep +go to washroom +take breakfast +go to school/college/office +Return from office +Take dinner +Go to sleep +``` + +এবার আমরা ধরলাম মিজান নামের একজনের জন্য এই কাজগুলো হবে। তাহলে মিজান সাহেবের জন্য আমরা কোডগুলো যদি প্রসিডিওরাল ওয়েতে লিখি তাহলে তা হবে এরকম। + +```txt +'Mizan', awake from sleep +'Mizan', go to washroom +'Mizan', take breakfast +'Mizan', go to school/college/office +'Mizan', Return from office +'Mizan', Take dinner +'Mizan', Go to sleep +``` + +এবার এই একই কাজ আকিব সাহেবের জন্যও লিখতে হবে। আমরা আকিব সাহেবের জন্যও লিখে ফেললাম। + +```txt +'Akib', awake from sleep +'Akib', go to washroom +'Akib', take breakfast +'Akib', go to school/college/office +'Akib', Return from office +'Akib', Take dinner +'Akib', Go to sleep +``` + +আবার এই একই কাজ ফাহিম সাহেবের জন্যও খাটে। তবে তিনি বাসায় বসে কাজ করেন। তাহলে অফিসে যাওয়া বা আসার কোনো ব্যাপার নেই এখানে। + +```txt +'Fahim', awake from sleep +'Fahim', go to washroom +'Fahim', take breakfast +'Fahim', work from home +'Fahim', Take dinner +'Fahim', Go to sleep +``` + +এই কাজ আবার জাভেদ সাহেবের জন্যও সত্যি। তিনি স্কুল, কলেজ, অফিস কোথাও যান না। তিনি শুধু ঘরে বসে পড়েন। + +```txt +'Javed', awake from sleep +'Javed', go to washroom +'Javed', take breakfast +'Javed', Study +'Javed', Take dinner +'Javed', Go to sleep +``` + +এখন এরকম যতজনের জন্য আসবে সবার জন্য আমাদের আলাদা আলাদা ভাবে সকল কোড পুনরায় লিখতে হবে। যদি আমরা এমন একটা টেমপ্লেট বানিয়ে রাখতে পারি যেখানে সব একই থাকবে শুধু নামটা চেইঞ্জ হবে তাহলে আমাদের জন্য সময়, শক্তি, অর্থ সব অনেকটা বেঁচে যায়। ধরুন আমরা একটা ফাংশন বানিয়ে নিলাম। কিভাবে বানাবো চলুন দেখি। + +```js +/** + * * Name: Human_Lifecycle + * * Param: human_name + * * :human_name, awake from sleep + * * :human_name, go to washroom + * * :human_name, take breakfast + * * :human_name, go to school/college/office + * * :human_name, Return from office + * * :human_name, Take dinner + * * :human_name", Go to sleep + */ +``` + +এখানে আমরা আমাদের ফাংশনের নাম দিলাম Human_Lifecycle এবং প্যারামিটার হিসেবে নিলাম human_name। এবার আমরা পূর্বে যেখানে যেখানে নাম দিয়েছিলাম সেখানে সরাসরি নাম না বসিয়ে প্যারামিটারটা বসিয়ে দিলাম। এবার এই ফাংশনটা আমরা যতজনের জন্য খুশি ততজনের জন্যই কল করতে পারবো জাস্ট এক লাইনের কোড লিখে। + +```js +// Call Human_Lifecycle for 'Abu Musa' +// Call Human_Lifecycle for 'Easin Islam' +// Call Human_Lifecycle for 'Saiful Islam' +// Call Human_Lifecycle for 'Akib Ahmed' +// Call Human_Lifecycle for 'Alamin Mir' +``` + +এবার দেখুন আমরা সবার জন্যই একই কাজ করছি কিন্তু আগের চেয়ে কম সময়ে কম কোড লিখে। + +কিন্তু এখন আরেকটা প্রব্লেম দেখা দিয়েছে। যেমন সবাই কিন্তু সব কাজ করে না। যেমন কেউ হয়তো অফিসে যায়, কেউ ঘরে বসে কাজ করে, কেউ কাজই করে না শুধু পড়ে। সেক্ষেত্রে এই ফাংশনটা সবার জন্য খাটবে না। আবার এখানে প্রতিটা কাজই আলাদা। কোনো কাজের সাথে কোনো কাজের সম্পর্ক নেই। আর প্রতিটা কাজ কিভাবে হবে তাও বলার দরকার আছে। যেমন ঘুমাতে যাওয়ার সাথে ওয়াশরুমে যাওয়ার কোনো সম্পর্ক নেই। আমি না ঘুমিয়েও ওয়াশরুমে যেতে পারি। আবার ডিনারের সাথেও ঘুমাতে যাওয়ার সম্পর্ক নেই। আমার আজ খিদা নেই আমি আজ না খেয়ে ঘুমালাম এমনও হতে পারে। তাহলে এখানে প্রতিটা কাজের জন্য আলাদা আলাদা ফাংশন তৈরি করে রেখে যার যে কাজ সেই কাজের ফাংশন কল করে দিলেই হয়ে গেলো। তাহলে আমরা ফাংশন উপরের মতো করে লিখবো না। আমরা লিখবো এভাবেঃ + +আমরা প্রথমে sleep এর জন্য একটা ফাংশন তৈরি করি। + +```js +/** + * Function: Sleep + * Param: name + * Definition: How to sleep + */ +``` + +এখানে আমরা ফাংশনের নাম নিলাম Sleep। প্যারামিটার হিসেবে নিলাম name। মানে কে ঘুমাচ্ছে। আর ডেফিনিশিন হলো কিভাবে ঘুমাচ্ছে। + +```js +/** + * Function: Awake + * Param: name + * Definition: How to awake + */ +``` + +এখানে আমরা জেগে উঠার একটা ফাংশন তৈরি করলাম আগের মতোই। + +```js +/** + * Function: Eat + * Param: name + * Param: Time + * Definition: How to eat + */ +``` + +এখানে আমরা খাওয়ার ফাংশন তৈরি করলাম। প্যারামিটার হিসেবে name এর সাথে time ও নিলাম। কারণ সে সকালের খাবার খাচ্ছে, নাকি দুপুরের খাবার খাচ্ছে নাকি রাতে খাবার খাচ্ছে সেটা এই প্যারামিটার দিয়ে আমরা বুঝবো। + +```js +/** + * Function: Go_To + * Param: name + * Param: Destination + * Param: Transport_system + * Definition: How to walk + */ +``` + +এরপর ধরলাম সে স্কুল, কলেজ বা অফিসে যাবে। সেটার জন্য একটা ফাংশন বানালাম। এখানে কে যাচ্ছে, কোথায় যাচ্ছে এবং কিভাবে যাচ্ছে, সে কি হেঁটে যাচ্ছে, বাসে যাচ্ছে, নিজের গাড়িতে যাচ্ছে এই তিনটা প্যারামিটার হিসেবে নিলাম। + +```js +/** + * Function: Work + * Param: name + * Definition: How to work + */ +``` + +এরপর কেউ ঘরে বসেও কাজ করতে পারে, অফিসে বসেও কাজ করতে পারে। কিন্তু কাজই করছে। সেক্ষেত্রে আমরা Work নামের একটা ফাংশন নিলাম। + +```js +/** + * Function: Study + * Param: name + * Definition: How to study + */ +``` + +Work এর মতো আমরা Study নামেও একটা ফাংশন নিলাম। + +এবার আমরা তিনটা ফাংশন তৈরি করবো। একটা যারা জব হোল্ডার তাদের জন্য, একটা ছাত্রছাত্রীদের জন্য, আরেকটা যারা রিমোট জব করেন তাদের জন্য। + +```js +/** + * Function: Job_Holder_Lifecycle + * Param: name + * Param: Transport_system + * Definition: + * - Awake -> name + * - Eat -> name, 'breakfast' + * - Go_To -> name, 'office', Transport_system + * - Work -> name + * - Eat -> name, 'lunch' + * - Go_To -> name, 'home', Transport_system + * - Eat -> name, 'dinner' + * - Sleep -> name + */ + +/** + * Function: Student_Lifecycle + * Param: name + * Param: Transport_system + * Definition: + * - Awake -> name + * - Eat -> name, 'breakfast' + * - Go_To -> name, 'institution', Transport_system + * - Study -> name + * - Eat -> name, 'lunch' + * - Go_To -> name, 'home', Transport_system + * - Eat -> name, 'dinner' + * - Sleep -> name + */ + +/** + * Function: Remote_Workers_Lifecycle + * Param: name + * Definition: + * - Awake -> name + * - Eat -> name, 'breakfast' + * - Work -> name + * - Eat -> name, 'lunch' + * - Work -> name + * - Eat -> name, 'dinner' + * - Sleep -> name + */ +``` + +আশা করি খুব সুন্দরভাবেই সবার মাথায় ঢুকে গেছে এখানে কি করেছি আমরা। একটা জায়গায় হয়তো কনফিউশন হচ্ছে, আমরা name, transport_system এখনও প্যারামিটার হিসেবে নিচ্ছি কিন্তু অন্য গুলো স্ট্যাটিকভাবে কেন নিচ্ছি? কারণ ধরেন breakfast, সে ঐ টাইমে ব্রেকফাস্টই করবে। সকালে সে কিন্তু lunch করবে না। তাই এটা আমরা স্ট্যাটিকভাবে ব্যবহার করেছি। কিন্তু আমরা জানিনা কার জন্য আমরা এটা ব্যবহার করবো, আর সে কিসে করে যাবে, তাই আমরা এখানে এখনও এই দুইটা প্যারামিটার হিসেবে রেখেছি। এবার আমরা ফাংশনগুলো কল করে দেখি। + +```js +// Students_Lifecycle -> 'Faruk' +// Students_Lifecycle -> 'Elias' +// Students_Lifecycle -> 'Faisal' + +// Job_Holder_Lifecycle -> 'Musa' +// Job_Holder_Lifecycle -> 'Akib' + +// Remote_Workers_Lifecycle -> 'Bipon' +``` + +আমরা যার জন্য যেমন ফাংশন দরকার সেটা খুব সুন্দরভাবে কল করতে পারলাম। তাহলে প্রতিটা কাজের বিপরীতে কিভাবে ফাংশন তৈরি করতে হবে এবং কি কি বিষয় মাথায় রাখতে হবে তার ছোটখাট একটা আইডিয়া আমরা পেয়ে গেলাম। + +প্রথমে আমরা জাভাস্ক্রিপ্তে ফাংশন কিভাবে লিখতে হবে তার একটা স্ট্রাকচার দেখি। + +```js +function name_of_the_function(/** Input something */) { + // Function body + // any valid js code + // return a result +} +``` + +এটা হচ্ছে ফাংশনের বেসিক স্ট্রাকচার। প্রথম `function` কীওয়ার্ড লিখবো। এরপরের ফাংশনের একটা নাম দিবো। এরপর আমরা () দিবো যার মধ্যে আমরা আমাদের প্যারামিটারগুলো দিবো। Param হিসেবে যেগুলো লিখেছিলাম সুডোকোডে সেগুলো। এরপর {} এর মধ্যে যা লিখবো সেটা হচ্ছে আমার ফাংশন বডি। ফাংশন বডির মধ্যে আমরা যেকোনো ভ্যালিড জাভাস্ক্রিপ্ট কোড লিখতে পারি। আর সবার শেষে ফাংশন কিছু না কিছু রিটার্ন করবে। কিছু রিটার্ন না করলেও `undefined` রিটার্ন করবে। এই স্ট্রাকচার ছাড়া আরো তিনভাবে ফাংশন স্ট্রাকচার লেখা যায়। ডিটেইলস আমরা দেখবো না। আমরা শুধু স্ট্রাকচারগুলো দেখি। একটা হলো Anonymous Function. মানে যে ফাংশনের কোনো নাম নেই। + +```js +function (/** Input something */) { + // Function body + // any valid js code + // return a result +} +``` + +আরেকটা হলো ফাংশন এক্সপ্রেশন হিসেবে একটা ভ্যারিয়েবলের মধ্যে রেখে লেখা। যেমনঃ + +```js +const name_of_the_function = function (/** Input something */) { + // Function body + // any valid js code + // return a result +}; +``` + +আরেকটা হলো যেটা জাভাস্ক্রিপ্ট ES6 এ এসেছে, অ্যারো ফাংশন। + +```js +const name_of_the_function = (/** Input something */) => { + // Function body + // any valid js code + // return a result +}; +``` + +আমরা আপাতত আমাদের বেসিক স্ট্রাকচারটাই ব্যবহার করবো। এখন ফাংশনের দুইটা স্টেপ আছে। + +1. Define a function +2. Invoke a function + +আমরা প্রথমে দেখি কিভাবে ফাংশন ডিফাইন করতে হবে। + +```js +function testFunction() { + const a = 10; + const b = 20; + const result = a + b + Math.max(a, b); + console.log(result); +} +``` + +এটা গেলো ফাংশন ডিফাইন। এবার দেখি কিভাবে ফাংশন call বা invoke করতে হবে। + +```js +testFunction(); // 50 +``` + +কিন্তু এই সিস্টেমের সমস্যা আছে একটা। যেমন যতবার এই ফাংশন কল হবে ততবারই এটা একই আউটপুট দিবে। আমি চাইলে আমার মনমতো ভ্যালু বাইরে থেকে এই ফাংশনে দিতে পারবো না। আবার ফাংশন বডির কোনো ডাটাও আমরা বাইরে থেকে এক্সেস পাবো না। ফাংশনের মধ্যে যে ভ্যারিয়েবল থাকে সেগুলো হলো লোকাল ভ্যারিয়েবল, ফাংশনের বাইরে হলে সেটা গ্লোবাল ভ্যারিয়েবল। সেটা নিয়ে আমরা পরবর্তীতে আলোচনা করবো। আমরা ফাংশনের ভিতরে যে ভ্যারিয়েবল নিয়েছি সেগুলো যদি () এর মধ্যে লিখি সেগুলোকে বলবো প্যারামিটার। এখন প্যারামিটার হিসেবে যা নিবো সেগুলো আমরা বাইরে থেকে ইনপুট দিতে পারবো। নিচের কোড দেখলে আরো ক্লিয়ার হবেঃ + +```js +function testFunction(a = 10, b = 20) { + const result = a + b + Math.max(a, b); + console.log(result); +} + +testFunction(); // 50 +testFunction(100, 200); // 500 +``` + +এখানে ফাংশন কল করার সময় আমরা যে ভ্যালুগুলো দিয়েছি সেগুলো আর্গুমেন্ট। আর প্যারামিটার হলো ফাংশন ডিফাইন করার সময় () এর মধ্যে যা দিবো। এখানে যদি কোনো আর্গুমেন্ট না দিই তাহলে সেটা যে ডিফল্ট ভ্যালু দেয়া আছে তা ধরে নিবে। আর যদি আর্গুমেন্ট দিই তাহলে সেগুলো নিবে। আমরা কোনো ডিফল্ট ভ্যালু নাও দিতে পারি। সেটা সম্পূর্ণ আমাদের ইচ্ছা। আশা করি ফাংশন, ফাংশন বডি, ফাংশন ডিফাইন, ফাংশন কল সম্পর্কে ধারণা ক্লিয়ার হয়েছে। এবার আমরা জাভাস্ক্রিপ্টে কিভাবে আমাদের ফাংশনের সুডোকোডগুলো কোডে রূপান্তরিত করতে পারবো তা দেখা যাক। + +```js +/** + * Function: Sleep + * Param: name + * Definition: How to sleep + */ + +function sleep(name) { + console.log(`${name} is sleeping`); +} + +/** + * Function: Awake + * Param: name + * Definition: How to awake + */ + +function awake(name) { + console.log(`${name} is awake`); +} + +/** + * Function: Eat + * Param: name + * Param: Time + * Definition: How to eat + */ + +function eat(name, time) { + console.log(`${name} is taking ${time}`); +} + +/** + * Function: Go_To + * Param: name + * Param: Destination + * Param: Transport_system + * Definition: How to walk + */ + +function goTo(name, destination, transport) { + console.log(`${name} is going to ${destination} by ${transport}`); +} + +/** + * Function: Work + * Param: name + * Definition: How to work + */ + +function work(name) { + console.log(`${name} is working`); +} + +/** + * Function: Study + * Param: name + * Definition: How to study + */ + +function study(name) { + console.log(`${name} is studying`); +} + +/** + * Function: Job_Holder_Lifecycle + * Param: name + * Param: Transport_system + * Definition: + * - Awake -> name + * - Eat -> name, 'breakfast' + * - Go_To -> name, 'office', Transport_system + * - Work -> name + * - Eat -> name, 'lunch' + * - Go_To -> name, 'home', Transport_system + * - Eat -> name, 'dinner' + * - Sleep -> name + */ + +function jobHolderLifecycle(name, transport) { + awake(name); + eat(name, 'breakfast'); + goTo(name, 'office', transport); + work(name); + eat(name, 'lunch'); + goTo(name, 'home', transport); + eat(name, 'dinner'); + sleep(name); +} + +/** + * Function: Student_Lifecycle + * Param: name + * Param: Transport_system + * Definition: + * - Awake -> name + * - Eat -> name, 'breakfast' + * - Go_To -> name, 'institution', Transport_system + * - Study -> name + * - Eat -> name, 'lunch' + * - Go_To -> name, 'home', Transport_system + * - Eat -> name, 'dinner' + * - Sleep -> name + */ + +function studentLifecycle(name, transport) { + awake(name); + eat(name, 'breakfast'); + goTo(name, 'institution', transport); + work(name); + eat(name, 'lunch'); + goTo(name, 'home', transport); + eat(name, 'dinner'); + sleep(name); +} + +/** + * Function: Remote_Workers_Lifecycle + * Param: name + * Definition: + * - Awake -> name + * - Eat -> name, 'breakfast' + * - Work -> name + * - Eat -> name, 'lunch' + * - Work -> name + * - Eat -> name, 'dinner' + * - Sleep -> name + */ + +function remoteWorkersLifecycle(name) { + awake(name); + eat(name, 'breakfast'); + work(name); + eat(name, 'lunch'); + work(name); + eat(name, 'dinner'); + sleep(name); +} + +console.log('Jobholders Lifecycle'); +console.log('**********************'); +jobHolderLifecycle('Shayed Hasan', 'bus'); +console.log('-----------------------'); +jobHolderLifecycle('Sh Pabel', 'bus'); +console.log('-----------------------'); +jobHolderLifecycle('Tarikul Islam', 'bus'); +console.log('-----------------------'); + +console.log('Remote Workers Lifecycle'); +console.log('**********************'); +remoteWorkersLifecycle('Nahian Sikder'); +console.log('-----------------------'); +remoteWorkersLifecycle('Mizan Rahman'); +console.log('-----------------------'); + +console.log('Students Lifecycle'); +console.log('**********************'); +studentsLifecycle('Faruk', 'rickshaw'); +console.log('--------------------'); +studentsLifecycle('Elias', 'rickshaw'); +console.log('--------------------'); +studentsLifecycle('Faisal', 'rickshaw'); +console.log('--------------------'); +``` + +আশা করি সবাই বুঝতে পেরেছেন ফাংশন সম্পর্কে। এভাবেই আমরা ক্ষুদ্র ক্ষুদ্র কাজকে ফাংশন বানিয়ে নিয়ে আমাদের প্রয়োজনমতো যেকোনো জায়গায় ব্যবহার করতে পারলাম। সব কোড বারবার লিখতে হলো না। + +## Function composition + +রিটার্ন নিয়ে সবাই একটু কনফিজড থাকে। কখন আমরা ফাংশনে রিটার্ন ব্যবহার করবো, কেন রিটার্ন করবো এসব নিয়ে। এটা আমরা বুঝতে পারবো যে টার্ম দিয়ে সেটা হলো function composition. কথাটা শুনে অনেকেরই ভয় লাগতে পারে এটা আবার কি? কিন্তু এটা আসলে নাইন টেনের গণিত। আমরা যখন ফাংশনের ম্যাথ করার সময় ব্যবহার করতাম f(g(x)), এর মানে হলো আমাদের কাছে একটা ফাংশন আছে g, যার মধ্যে x দিলে তা কোনো না কোনো রেজাল্ট রিটার্ন করবে যেটা আমরা ইউজ করবো f ফাংশনের মধ্যে। মানে কোনো ফাংশন থেকে যে রেজাল্ট আমরা পাবো সেটা অন্য আরেকটা ফাংশনে ইউজ করা, একেই বলে ফাংশন কম্পোজিশন। এই চেহার দিয়ে বুঝাটা একটু কঠিন। আমরা ছোট ছোট কয়েকটা ফাংশন বানিয়ে ফেলি বুঝার সুবিধার্থে। + +```js +function sum(a, b) { + console.log(a + b); +} + +function subtract(a, b) { + console.log(a - b); +} + +function times(a, b) { + console.log(a * b); +} + +sum(10, 20); // 30 +sub(10, 20); // -10 +times(10, 20); // 200 +``` + +এরা আমাকে সুন্দরভাবে রেজাল্ট দিচ্ছে। এখানে কোনো প্রব্লেম নেই। প্রব্লেমটা শুরু হবে নিচের কোডটাতে। + +```js +function sum(a, b) { + console.log(a + b); +} + +function subtract(a, b) { + console.log(a - b); +} + +function times(a, b) { + console.log(a * b); +} + +const a = 10; +const b = 20; + +const r1 = sum(a, b); // 30 +console.log('R1', r1); // 'R1' undefined +const r2 = subtract(a, b); // -10 +console.log('R2', r2); // 'R2' undefined +const r = times(r1, r2); // NaN +console.log(r); // undefined +``` + +এখানে এমন রেজাল্ট আসলো কেন? যখন আমরা কোনো ফাংশনের রেজাল্ট কোনো ভ্যারিয়েবলে স্টোর করে রাখতে চাইবো, তখন অবশ্যই ঐ ফাংশনকে কিছু না কিছু রিটার্ন করতে হবে। এখানে আমাদের ফাংশন কিছুই রিটার্ন করছে। console.log() কিছুই রিটার্ন করে না। তাহলে যদি কিছু রিটার্ন না করে তাহলে এখানে undefined আসলো কোথা থেকে? আমাদের মনে রাখতে হবে জাভাস্ক্রিপ্ট ফাংশনে আমরা যদি বলে না দিই কি রিটার্ন করতে হবে তাহলে তা বাই ডিফল্ট undefined রিটার্ন করবে। সুতরাং আমাদের r1 ভ্যারিয়েবলে স্টোর হলো undefined, r2 তে স্টোর হলো undefined। এখন যদি undefined এর সাথে undefined গুণ করি তাহলে রেজাল্ট আসবে NaN, কারণ দুইটার কোনোটাই নাম্বার না। আর যেহেতু times() ও কিছু রিটার্ন করেনি তাই r এ স্টোর হলো undefined. এই সমস্যা থেকে মুক্তি পেতে হলে আমাদের console.log() এর পরিবর্তে ব্যবহার করতে হবে return। মনে রাখতে হবে যখনই আমাদের কোনো ফাংশনের রেজাল্ট কোথাও স্টোর করার প্রয়োজন হবে বা কোথাও ব্যবহার করার প্রয়োজন হবে তখন অবশ্যই অবশ্যই return ব্যবহার করতে হবে। আমরা একটু return দিয়ে দেখি। + +```js +function sum(a, b) { + return a + b; +} + +function subtract(a, b) { + return a - b; +} + +function times(a, b) { + return a * b; +} + +const a = 10; +const b = 20; + +const r1 = sum(a, b); +console.log('R1', r1); // 'R1' 30 +const r2 = subtract(a, b); +console.log('R2', r2); // 'R1' -10 +const r = times(r1, r2); +console.log(r); // -300 +``` + +আমরা এতো ভ্যারিয়েবল না নিয়ে এভাবেও লিখতে পারি। + +```js +const r = Math.abs(times(sum(a, b), subtract(a, b))); +console.log(r); // 300 +``` + +Math.abs() ব্যবহার করা হয়েছে রেজাল্ট পজিটিভ আনার জন্য। আমরা যদি Math.abs() = f(), times() = g(), sum(a, b) = x, sub(a, b) = y ধরি তাহলে চেহারাটা দাঁড়ায় f(g(x, y))। এটাই function composition। ছোটবেলার ম্যাথমেটিক্স। আশা করি আপনারা বুঝতে পেরেছেন। + +এতক্ষণ পর্যন্ত আমরা যা যা শিখেছি তা হলোঃ + +- Function definition +- Function Invoking +- Function Parameters/Arguments +- Return result from function + +ফাংশন সম্পর্কে মোটামুটি আমরা জেনেছি। এবার আমরা যাবো ফাংশনাল প্রোগ্রামিং এ। + +## Functional as a value + +জাভাস্ক্রিপ্টে ফাংশনাল প্রোগ্রামিং জিনিসটা কি যদি প্রশ্ন করা হয় তাহলে এক কথায় বলতে হয়, 'Function is a first class citizen'. এখন এই `first class citizen` বলতে কী বুঝানো হয়েছে? সে কি খুব বড় মাপের কিছু? ব্যাপারটা মোটেও সেরকম নয়। কোনো প্রোগ্রামিং ল্যাঙ্গুয়েজকে ফাংশনাল প্রোগ্রামিং ল্যাঙ্গুয়েজ রূপে অধিষ্ঠিত হতে হলে তাকে যে শর্ত পূরণ করতে হয় তা হলো, 'We can treat function as value'। যদি ফাংশনকে আমরা ভ্যালু হিসেবে ট্রিট করতে পারি কোনো ল্যাঙ্গুয়েজে, তাহলে সেই ল্যাঙ্গুয়েজকে আমরা ফাংশনাল প্রোগ্রামিং ল্যাঙ্গুয়েজ হিসেবে বলতে পারি। ভ্যালু বলতে যেমন `10`, `'test'`, `true` এসব ভ্যালুকে আমরা যেভাবে ট্রিট করতে পারি ফাংশনকেও আমরা সেভাবে ট্রিট করতে পারবো ফাংশনাল প্রোগ্রামিং এ। যদি পারি তাহলে সেই ফাংশন সেই ল্যাঙ্গুয়েজের first class citizen. এখন এটা হওয়ার সুবিধা কি কি? + +- we can store functions in a variable +- we can store function inside an object / array +- we can pass function as an argument +- we can also return a function from another function + +যারা OOP থেকে এসেছেন তাদের কাছে ফাংশনাল প্রোগ্রামিং একটা ইউজলেস বলে মনে হতে পারে। কিন্তু যতোই দিন যাবে আপনি এর প্রেমে পড়ে যাবেন। এর সবথেকে বড় সুবিধা হলো OOP তে প্রোগ্রামিং করতে হয় ইম্পেরেটিভ ওয়েতে। মানে শুরু থেকে শেষ সব কোড লিখতে হয়। কিন্তু ফাংশনাল প্রোগ্রামিং এ করতে হয় ডিক্লারেটিভ ওয়েতে। তার মানে অনেক বিজনেস লজিক আমার জানার প্রয়োজনই নেই। শুধু যতটুকু প্রয়োজন ততটুকু জানলেই চলছে। দ্বিতীয় আরেকতা বড় সুবিধা হলো, OOP তে প্রোগ্রামিং করা অনেক এক্সপেন্সিভ। এক্সপেন্সিভ বলতে আপনাকে টাকা দিয়ে কিনতে হবে ব্যাপারটা এমন না। এর মানে হলো এখানে প্রচুর মেমোরি ইউজ হবে, অনেক টাইম কমপ্লেক্সিটি আসবে। সেখানে ফাংশন অনেক হালকা। যেমন ReactJS যখন প্রথম এসেছিলো তখন তারা ক্লাস বেইজড ছিল। কিন্তু পরে দেখলো এতে পারফরম্যান্স ঠিকমতো অপটিমাইজড হচ্ছে না। আর ডেভেলপাররাও অনেক ইস্যু ফেইস করছেন। তখন তারা চলে গেলো ফাংশনাল প্রোগ্রামিং এ। এটা অনেক লাইটওয়েট, সহজে শেখা যায়, সহজে ব্যবহার করা যায় এবং সহজে মেইনটেইন করা যায়, পারফরম্যান্সও ভাল। তৃতীয় আরেকটা বিষয় হলো OOP তে আমরা যেহেতু অবজেক্টের প্রোপার্টি এবং মেথড দিয়ে দিচ্ছি, সেখানে বারবার সেই মেথডকেই আমরা আপডেট করছি। তার মানে এটা মিউটেবল। কিছু কিছু ক্ষেত্রে মিউটেবল উপকারী, কিছু কিছু ক্ষেত্রে খুবই জঘন্য। কিন্তু ফাংশনাল প্রোগ্রামিং সবসময় ইমমিউটেবল। যদিও আমরা মিউটেবিলিটি নিয়ে কাজ করতে পারি। কিন্তু বেশির ভাগ ক্ষেত্রেই আমরা ইমমিউটেবলের জন্যই এটা ব্যবহার করবো। এবার আমরা যে যে বেনিফিটের কথা বলেছিলাম তা একে একে বুঝবো। এবং প্রমাণ করবো ফাংশন একটা ভ্যালু। + +```js +function testFunction() { + console.log('I am a test function'); +} + +const fn = testFunction; +console.log(fn); +fn(); // I am a test function +``` + +প্রথমে আমরা একটা ফাংশন ডিফাইন করলাম। এটা কিছু রিটার্ন করছে না। তাই যদি আমরা ভ্যারিয়েবলে স্টোর করার সময় testFunction এর শেষে () দিই তাহলে তা undefined রিটার্ন করবে। কিন্তু আমি চাইছি ফাংশনটা স্টোর করার জন্য, তার রেজাল্ট না। তাই আমাদের () ছাড়া শুধু testFunction লিখতে হবে। এখন যদি আমরা testFunction এর জায়গায় fn কে কল করি তাহলে রেজাল্ট আসবে 'I am a test function'। তাহলে আমরা ফাংশনকে ভ্যালু হিসেবে ভ্যারিয়েবলে স্টোর করতে পারছি। + +এবার যেহেতু ভ্যারিয়েবলে স্টোর করতে পেরেছি সেহেতু অবজেক্ট বা অ্যারের মধ্যেও আমরা স্টোর করতে পারবো। + +```js +const arr = [fn, testFunction]; +const obj = { + fn: testFunction, +}; +``` + +আমরা ফাংশনকে আর্গুমেন্ট হিসেবেও পাস করতে পারবো। + +```js +function fnArgument(fn) { + return fn(); +} +fnArgument(testFunction); +``` + +এমনকি আমরা এক ফাংশন থেকে অন্য আরেকটা ফাংশনকেও রিটার্ন করতে পারি। + +```jsx +function returnFn() { + return testFunction; +} +``` + +তাহলে দেখলাম ফাংশনকে ভ্যালু হিসেবে সব কাজই করা যায়। কিন্তু আমরা যেমন new Object(), new Array() এভাবে লিখে অবজেক্ট এবং অ্যারে ডিফাইন করতে পারতাম, যদি new Function() লিখে ফাংশন বানাতে না পারি তাহলে আমরা কেন মানবো এটা যে ফাংশন? কারণ জাভাস্ক্রিপ্টে প্রতিটা ভ্যালু তৈরি করার জন্য একটা কনস্ট্রাক্টর আছে। আসুন এবার তাহলে আমরা ফাংশন কন্সট্রাক্ট করি। + +## Function Construction + +ফাংশন কনস্ট্রাকশন বুঝার আগে আমরা একটু আগে নিচের ফাংশনটা দেখি। + +```js +function strToObj(str) { + let obj = {}; + for (let s of str) { + if (s !== ' ') { + obj[s] = s; + } + } + return obj; +} +console.log(strToObj('HM Nayem')); // { H: 'H', M: 'M', N: 'N', a: 'a', y: 'y', e: 'e', m: 'm' } +``` + +এবার এই ফাংশনকে আমরা কন্সট্রাক্টরের মাধ্যমে বানাবো। + +```js +const fn = new Function( + 'str', + `let obj = {}; + for (let s of str) { + if (s !== ' ') { + obj[s] = s; + } + } + return obj;` +); + +console.log(fn('HM Nayem')); // { H: 'H', M: 'M', N: 'N', a: 'a', y: 'y', e: 'e', m: 'm' } +``` + +আগের ফাংশনে যেভাবে কাজ করেছে ঠিক একই কাজ করছে কনস্ট্রাক্টর দিয়ে ফাংশন বানানোর পর। new Function() এ আর্গুমেন্ট আকারে যা খুশি তা যতো খুশি ততো দেয়া যাবে। কিন্তু মনে রাখতে হবে শেষ আর্গুমেন্ট হতে হবে অবশ্যই ফাংশন বডি অর্থাৎ {} এর মধ্যে যা থাকবে তা। আমরা এখানে প্রথ আর্গুমেন্ট হিসেবে ব্যবহার করেছি আমাদের ফাংশন প্যারামিটার, আর সেকেন্ড আর্গুমেন্ট হিসেবে দিয়েছি ফাংশন বডি। প্রোগ্রাম রান করলে দেখা যাচ্ছে একই আউটপুট দিচ্ছে তা। তাহলে আমরা কনস্ট্রাক্টর ব্যবহার করে ফাংশন তৈরি করতে পারছি। তাহলে ফাংশনকে first class citizen বলাতে আর কোনো বাধা থাকলো না। এটা একটা বড় সুবিধা প্রদান করে মেটা প্রোগ্রামিং এর ক্ষেত্রে। + +মেটা প্রোগ্রামিং একটা দারুণ কনসেপ্ট। এর সুবিধা হচ্ছে আপনি ডায়নামিকভাবে রানটাইমে নতুন নতুন কোড জেনারেট করতে পারবেন। আপনার মতো করে আপনি সিনট্যাক্স তৈরি করতে পারবেন। আপনার জাভাস্ক্রিপ্টে কোনো সিনট্যাক্স ভাল লাগছে না? আপনি আপনার নিজের সিনট্যাক্স তৈরি করতে পারবেন। ধরেন জাভাস্ক্রিপ্টে অ্যারে ডিফাইন করা হয় এভাবে `const arr = []`. আপনি এভাবে চাইছেন না। আপনি চাইছেন ডাটা টাইপসহ আপনি ডিফাইন করে দিবেন ঠিক এইভাবে `const [] number: arr`. সেটাও পারবেন এই মেটা প্রোগ্রামিং এর দ্বারা। মেটা প্রোগ্রামিং নিয়ে বিস্তারিত জানতে [Meta Programming](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Meta_programming) এই আর্টিকেলটি পড়ুন। + +ফাংশন কন্সট্রাক্টরের আরেকটি বড় সুবিধা হলো যেহেতু আপনি এভাবে ফাংশন বানাতে পারছেন সেহেতু আপনি চাইলে রানটাইমেও ফাংশন জেনারেট করতে পারবেন। + +ধরেন আমাদের সার্ভারে একটা CMS আছে। সেখান থেকে ফাংশনের বডি তৈরি করে আমরা ক্লায়েন্টের কাছে পাঠিয়ে দিবো, এটা হলো আমাদের টার্গেট। আমরা একটা অবজেক্ট তৈরি করি এভাবে। + +```js +const fData = { + params: ['a', 'b'], + body: ['const r = a + b', 'return r'], +}; +``` + +এখন বডিতে প্রতিটা লাইন আলাদা আলাদাভাবে আছে। আমাদের সেখান থেকে একসাথে আনতে হবে। সেটা আমরা করতে পারি reduce মেথড দ্বারা। + +```js +const fBody = fData.body.reduce((acc, cur) => { + acc += cur + ';'; + return acc; +}, ''); +``` + +গত ক্লাসে reduce নিয়ে ডিটেইলস আলোচনা করা হয়েছে। আপনারা ওখান থেকে ভালভাবে বুঝে নিন। এখন আমরা sum করার ফাংশন কনস্ট্রাক্ট করি কনস্ট্রাক্টর দিয়ে। + +```js +const tf = new Function(...fData.params, fBody); +console.log(tf(100, 200)); // 300 +``` + +How powerful it is! আমরা এমন একটা লাইব্রেরি বানিয়ে ফেলতে পারি যেই লাইব্রেরি দিয়ে আমরা json থেকে ফাংশন বানিয়ে ফেলতে পারি। CMS দিয়ে যে ফাংশন দরকার সে ফাংশন রেডি করে দিয়ে আসতে পারবেন। ডায়নামিকভাবে ফাংশন বিহেভিয়ার চেইঞ্জ করতে পারবেন। আমরা JSON থেকে ডায়নামিকভাবে একটা ফাংশন জেনারেট করে ফেললাম। + +আরেকটা উদাহরণ দিই। ধরুন আমরা পুরো নাম থেকে ফার্স্টনেইম বের করে আনার ফাংশন কনস্ট্রাক্ট করতে চাই। + +```js +const greetFn = new Function( + 'name', + 'email', + ` + const fName = name.split(' ')[0]; + console.log('Hello,', fName, 'Is that your email?', email); + console.log('Yeah, this is mine.'); + return fName; + ` +); + +console.log(typeof greetFn); // function +console.log(greetFn.toString()); +/* +function anonymous(name,email) { + const fName = name.split(' ')[0]; + console.log('Hello,', fName, 'Is that your email?', email); + console.log('Yeah, this is mine.'); + return fName; +} +*/ +const fName = greetFn('HM Nayem', 'nayem@gmail.com'); +/* +Hello, HM Is that your email? nayem@gmail.com +Yeah, this is mine. +*/ +console.log('First Name:', fName); // First Name: HM +``` + +আশা করি সবাই কোড দেখে বুঝতে পেরেছেন। greetFn এর টাইপ দেখাচ্ছে function. ফাংশন আমরা এভাবে তৈরি করিনি। কিন্তু ওরা ওদের মতো করে বানিয়ে নিয়েছে। আর আউটপুট যেভাবে দেয়ার সেভাবে দিয়েছে। + +এতক্ষণ যা করলাম তা কিন্তু ফাংশনাল প্রোগ্রামিং না। আমরা জাস্ট ফাংশন যে একটা ভ্যালু তা প্রমাণ করলাম। আর ফাংশনাল প্রোগ্রামিং ল্যাঙ্গুয়েজ হওয়ার জন্য অবশ্যই ফাংশন ভ্যালু হিসেবে ট্রিট হতে হবে। + +এবার আমরা একটা মজার জিনিস দেখি। সেটা হলো আমরা কিছু অপারেশনের আর্গুমেন্টস, প্যারামিটারস, ফাংশন বডি একটা অ্যারেতে স্টোর করে রাখলাম। + +```js +const operations = [ + { + args: [10, 20], + params: ['a', 'b'], + body: 'console.log("a + b",a + b)', + }, + { + args: [10, 20], + params: ['a', 'b'], + body: 'console.log("a - b",a - b)', + }, + { + args: [10, 20], + params: ['a', 'b'], + body: 'console.log("a * b",a * b)', + }, +]; +``` + +এবার যদি আমরা forEach মেথড চালিয়ে প্রতিটা অপারেশনকে ফাংশন কনস্ট্রাক্টরের মাধ্যমে ফাংশন বানিয়ে আউটপুট দেখার চেষ্টা করি তাহলে কি দেখা যায় দেখি। + +```js +operations.forEach((operation) => { + const fn = new Function(...operation.params, operation.body); + fn(...operation.args); +}); +/* +a + b 30 +a - b -10 +a * b 200 +*/ +``` + +এবার আশা করি বুঝতে পেরেছেন এটা কতটা পাওয়ারফুল একটা টার্ম। এটা যদিও আমরা কোনো রিয়েল অ্যাপ্লিকেশন ডেভেলপমেন্টে ব্যবহার করবো না। কারণ সিকিউরিটি ল্যাক আছে এতে। আমরা যখন সার্ভার থেকে ফাংশন বডি নিবো তখন এই সাইডটা আমাদের ওপেন রাখতে হচ্ছে। এখন হ্যাকার যদি কোনো কারণে আমার এপিআই চেইঞ্জ করতে পারে, তাহলে সেক্ষেত্রে তার কোড আমার সিস্টেমে এসে আমার সিস্টেমকে হযবরল করে দিতে পারে। এটা আমরা জাস্ট ফান করার জন্য, বুঝার জন্য করেছি। রিয়েল অ্যাপ্লিকেশন ডেভেলপমেন্টে এর কোনো কাজ নেই। + +ফাংশন নিয়ে অনেক বিস্তারিত আলোচনা এতে করা হয়েছে। আশা করা যায় সবাই ভালভাবে বুঝতে পেরেছেন। + + + +## Source Code + +এই লেকচারের সমস্ত সোর্স কোড এই [লিংকে](../../../src/lecture-08/app.js) এ পাবেন। + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/docs/Lectures/Fundamentals/08/Resources.md b/docs/Lectures/Fundamentals/08/Resources.md new file mode 100644 index 0000000..7323270 --- /dev/null +++ b/docs/Lectures/Fundamentals/08/Resources.md @@ -0,0 +1,365 @@ +## Lecture 8 - Understand JavaScript Functions | Function as a value + +> **JavaScript is an Impure Functional Programming Language. Because we can use oop, procedural way in JavaScript.** + +- **Function Template** + + ```jsx + function name_of_the_function(/** Input something */) { + // Function body + // any valid js code + // return a result + } + ``` + +- **Function Pseudocode** + + ```jsx + /** + * * Name: Human_Lifecycle + * * Param: human_name + * * :human_name, awake from sleep + * * :human_name, go to washroom + * * :human_name, take breakfast + * * :human_name, go to school/college/office + * * :human_name, Return from office + * * :human_name, Take dinner + * * :human_name", Go to sleep + */ + + // Call Human_Lifecycle for 'Abu Musa' + // Call Human_Lifecycle for 'Easin Islam' + // Call Human_Lifecycle for 'Saiful Islam' + // Call Human_Lifecycle for 'Akib Ahmed' + // Call Human_Lifecycle for 'Alamin Mir' + + /** + * Function: Sleep + * Param: name + * Definition: How to sleep + */ + + /** + * Function: Awake + * Param: name + * Definition: How to awake + */ + + /** + * Function: Eat + * Param: name + * Param: Time + * Definition: How to eat + */ + + /** + * Function: Walk + * Param: name + * Param: Destination + * Definition: How to walk + */ + + /** + * Function: Study + * Param: name + * Definition: How to study + */ + + /** + * Function: Work + * Param: name + * Definition: How to work + */ + + /** + * Function: Job_Holder_Lifecycle + * Param: name + * Definition: + * - Awake -> name + * - Eat -> name, 'breakfast' + * - Walk -> name, 'office' + * - Work -> name + * - Eat -> name, 'lunch' + * - Walk -> name, 'home' + * - Eat -> name, 'dinner' + * - Sleep -> name + */ + + /** + * Function: Student Lifecycle + * Param: name + * Definition: + * - Awake -> name + * - Eat -> name, 'breakfast' + * - Study -> name + * - Eat -> name, 'lunch' + * - Study -> name + * - Eat -> name, 'dinner' + * - Sleep -> name + */ + + // Students_Lifecycle -> 'Faruk' + // Students_Lifecycle -> 'Elias' + // Students_Lifecycle -> 'Faisal' + + // Job_Holder_Lifecycle -> 'Musa' + // Job_Holder_Lifecycle -> 'Akib' + // Job_Holder_Lifecycle -> 'Bipon' + ``` + +- **Function code of above psuedocode** + + ```jsx + function sleep(name) { + console.log(`${name} is sleeping`); + } + + function awake(name) { + console.log(`${name} is awake`); + } + + function eat(name, time) { + console.log(`${name} is taking ${time}`); + } + + function walk(name, destination) { + console.log(`${name} is walking to ${destination}`); + } + + function study(name) { + console.log(`${name} is studying`); + } + + function work(name) { + console.log(`${name} is working`); + } + + function jobHolderLifecycle(name) { + awake(name); + eat(name, 'breakfast'); + walk(name, 'office'); + work(name); + eat(name, 'lunch'); + walk(name, 'home'); + eat(name, 'dinner'); + sleep(name); + } + + function studentsLifecycle(name) { + awake(name); + eat(name, 'breakfast'); + study(name); + eat(name, 'lunch'); + study(name); + eat(name, 'dinner'); + sleep(name); + } + ``` + +- **Steps of a function** + + - **There are two steps of a function** + + - **Define a function** + + ```jsx + function testFunction() { + const a = 10; + const b = 20; + const result = a + b + Math.max(a, b); + console.log(result); + } + ``` + + - **Invoke a function** + + ```jsx + testFunction(); + ``` + +There are some problems in above defined function. We can't use the function for any value. For this reason we need to use the parameters. + +```jsx +function testFunction(a = 10, b = 20) { + const result = a + b + Math.max(a, b); + console.log(result); +} +``` + +a and b are parameters. 10 and 20 are default values. If we don't pass any arguments in the function, it will take the default values as arguments. Now, what is arguments? Please see the below to learn that: + +```jsx +testFunction(100, 200); // Here 100 and 200 are arguments +``` + +- **Function Composition** + + ```jsx + function sum(a, b) { + return a + b; + } + + function subtract(a, b) { + return a - b; + } + + function times(a, b) { + return a * b; + } + + const a = 10; + const b = 20; + + // const r1 = sum(a, b); + // console.log('R1', r1); + // const r2 = subtract(a, b); + // console.log('R2', r2); + const r = Math.abs(times(sum(a, b), subtract(a, b))); + console.log(r); + ``` + +Function is a first class citizen. Because we can treat function as a value. + +- **Benefits of a function treat as a value:** + + - **we can store functions in a variable** + + ```jsx + function testFunction() { + console.log('I am a test function'); + } + + const fn = testFunction; + console.log(fn.toString()); + fn(); + ``` + + - **we can store function inside an object / array** + + ```jsx + const arr = [fn, testFunction]; + const obj = { + fn: testFunction, + }; + ``` + + - **we can pass function as an argument** + + ```jsx + function fnArgument(fn) { + return fn(); + } + fnArgument(testFunction); + ``` + + - **we can also return a function from another function** + + ```jsx + function returnFn() { + return testFunction; + } + ``` + +- **Function Construction** + + ```jsx + const newFn = new Function( + 'str', + `let obj = {}; + for (let s of str) { + if (s !== ' ') { + obj[s] = s; + } + } + return obj;` + ); + + console.log(newFn('HM Nayem')); + ``` + +On the above code, we can pass arguments as many as we want. But last argument must be the function body. If we don't pass the body as last argument it will throw an error. + +**More examples of function construction:** + +```jsx +const fData = { + params: ['a', 'b'], + body: ['const r = a + b', 'return r'], +}; + +const fBody = fData.body.reduce((acc, cur) => { + acc += cur + ';'; + return acc; +}, ''); + +const tf = new Function(...fData.params, fBody); +console.log(tf(100, 200)); +``` + +```jsx +const greetFn = new Function( + 'name', + 'email', + ` + const fName = name.split(' ')[0]; + console.log('Hello,', fName, 'Is that your email?', email); + console.log('Yeah, this is mine.'); + return fName; + ` +); + +console.log(typeof greetFn); +console.log(greetFn.__proto__); +// console.log(greetFn.toString()); +const fName = greetFn('HM Nayem', 'nayem@gmail.com'); +console.log('First Name:', fName); +``` + +```jsx +const operations = [ + { + args: [10, 20], + params: ['a', 'b'], + body: 'console.log("a + b",a + b)', + }, + { + args: [10, 20], + params: ['a', 'b'], + body: 'console.log("a - b",a - b)', + }, + { + args: [10, 20], + params: ['a', 'b'], + body: 'console.log("a * b",a * b)', + }, + { + args: [], + params: [], + body: 'console.log("Hello World! No params, no args")', + }, + { + args: [5], + params: ['n'], + body: ` + for (let i = 0; i < n; i++) { + let line = ''; + for (let j = 0; j < n; j++) { + line += '* '; + } + console.log(line); + } + `, + }, +]; + +operations.forEach((operation) => { + const fn = new Function(...operation.params, operation.body); + fn(...operation.args); +}); +``` + +## Important Links + +- [Meta Programming](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Meta_programming) +- [Source Code of this class](../../src/lecture-08/app.js) +- [Class Overview](../../class-overview/Lecture-08/README.md) diff --git a/docs/Lectures/Fundamentals/08/SourceCode.md b/docs/Lectures/Fundamentals/08/SourceCode.md new file mode 100644 index 0000000..6b000df --- /dev/null +++ b/docs/Lectures/Fundamentals/08/SourceCode.md @@ -0,0 +1,346 @@ +
+ App.js +

This Source Code of App.Js

+ +```javascript +/** + * * Name: Human_Lifecycle + * * Param: human_name + * * :human_name, awake from sleep + * * :human_name, go to washroom + * * :human_name, take breakfast + * * :human_name, go to school/college/office + * * :human_name, Return from office + * * :human_name, Take dinner + * * :human_name", Go to sleep + */ + +// Call Human_Lifecycle for 'Abu Musa' +// Call Human_Lifecycle for 'Easin Islam' +// Call Human_Lifecycle for 'Saiful Islam' +// Call Human_Lifecycle for 'Akib Ahmed' +// Call Human_Lifecycle for 'Alamin Mir' + +/** + * Function: Sleep + * Param: name + * Definition: How to sleep + */ + +function sleep(name) { + console.log(`${name} is sleeping`); +} + +/** + * Function: Awake + * Param: name + * Definition: How to awake + */ + +function awake(name) { + console.log(`${name} is awake`); +} + +/** + * Function: Eat + * Param: name + * Param: Time + * Definition: How to eat + */ + +function eat(name, time) { + console.log(`${name} is taking ${time}`); +} + +/** + * Function: Walk + * Param: name + * Param: Destination + * Definition: How to walk + */ + +function walk(name, destination) { + console.log(`${name} is walking to ${destination}`); +} + +/** + * Function: Study + * Param: name + * Definition: How to study + */ + +function study(name) { + console.log(`${name} is studying`); +} + +/** + * Function: Work + * Param: name + * Definition: How to work + */ + +function work(name) { + console.log(`${name} is working`); +} + +/** + * Function: Job_Holder_Lifecycle + * Param: name + * Definition: + * - Awake -> name + * - Eat -> name, 'breakfast' + * - Walk -> name, 'office' + * - Work -> name + * - Eat -> name, 'lunch' + * - Walk -> name, 'home' + * - Eat -> name, 'dinner' + * - Sleep -> name + */ + +function jobHolderLifecycle(name) { + awake(name); + eat(name, 'breakfast'); + walk(name, 'office'); + work(name); + eat(name, 'lunch'); + walk(name, 'home'); + eat(name, 'dinner'); + sleep(name); +} + +console.log('Jobholders Lifecycle'); +console.log('**********************'); +jobHolderLifecycle('Shayed Hasan'); +console.log('-----------------------'); +jobHolderLifecycle('Sh Pabel'); +console.log('-----------------------'); +jobHolderLifecycle('Tarikul Islam'); +console.log('-----------------------'); +jobHolderLifecycle('Nahian Sikder'); +console.log('-----------------------'); +jobHolderLifecycle('Mizan Rahman'); +console.log('-----------------------'); + +/** + * Function: Student Lifecycle + * Param: name + * Definition: + * - Awake -> name + * - Eat -> name, 'breakfast' + * - Study -> name + * - Eat -> name, 'lunch' + * - Study -> name + * - Eat -> name, 'dinner' + * - Sleep -> name + */ + +function studentsLifecycle(name) { + awake(name); + eat(name, 'breakfast'); + study(name); + eat(name, 'lunch'); + study(name); + eat(name, 'dinner'); + sleep(name); +} + +console.log('Students Lifecycle'); +console.log('**********************'); +studentsLifecycle('Faruk'); +console.log('--------------------'); +studentsLifecycle('Elias'); +console.log('--------------------'); +studentsLifecycle('Faisal'); +console.log('--------------------'); + +// Students_Lifecycle -> 'Faruk' +// Students_Lifecycle -> 'Elias' +// Students_Lifecycle -> 'Faisal' + +// Job_Holder_Lifecycle -> 'Musa' +// Job_Holder_Lifecycle -> 'Akib' +// Job_Holder_Lifecycle -> 'Bipon' + +// * Function Template +function name_of_the_function(/** Input something */) { + // Function body + // any valid js code + // return a result +} + +// There are two steps +// - Define a function +// - Invoke a function + +/* function testFunction() { + const a = 10; + const b = 20; + const result = a + b + Math.max(a, b); + console.log(result); +} */ + +function testFunction(a = 10, b = 20) { + const result = a + b + Math.max(a, b); + console.log(result); +} + +// testFunction(100, 200); +// testFunction(50, 30); +// testFunction(5); +// testFunction(); + +// function composition +function sum(a, b) { + return a + b; +} + +function subtract(a, b) { + return a - b; +} + +function times(a, b) { + return a * b; +} + +const a = 10; +const b = 20; + +// const r1 = sum(a, b); +// console.log('R1', r1); +// const r2 = subtract(a, b); +// console.log('R2', r2); +const r = Math.abs(times(sum(a, b), subtract(a, b))); +console.log(r); + +// Function definition +// Function Invoking +// Function Parameters/Arguments +// Return result from function + +// Function is a first class citizen +// We can treat function as value +// 10, 'test', true, false -> function + +/** + * * Benefits: + * * - we can store functions in a variable + * * - we can store function inside an object / array + * * - we can pass function as an argument + * * - we can also return a function from another function + */ + +// * Proof -> Function is a value +function testFunction() { + console.log('I am a test function'); +} + +// * store function in a variable +const fn = testFunction; +console.log(fn.toString()); +fn(); + +// * store function inside an object / array +const arr = [fn, testFunction]; +const obj = { + fn: testFunction, +}; + +// * pass function as an argument +function fnArgument(fn) { + return fn(); +} +fnArgument(testFunction); + +// * return a function from another function +function returnFn() { + return testFunction; +} + +// * Let's construct a function +const newFn = new Function( + 'str', + `let obj = {}; + for (let s of str) { + if (s !== ' ') { + obj[s] = s; + } + } + return obj;` +); + +console.log(newFn('HM Nayem')); + +const fData = { + params: ['a', 'b'], + body: ['const r = a + b', 'return r'], +}; + +const fBody = fData.body.reduce((acc, cur) => { + acc += cur + ';'; + return acc; +}, ''); + +const tf = new Function(...fData.params, fBody); +console.log(tf(100, 200)); + +const greetFn = new Function( + 'name', + 'email', + ` + const fName = name.split(' ')[0]; + console.log('Hello,', fName, 'Is that your email?', email); + console.log('Yeah, this is mine.'); + return fName; + ` +); + +console.log(typeof greetFn); +console.log(greetFn.__proto__); +// console.log(greetFn.toString()); +const fName = greetFn('HM Nayem', 'nayem@gmail.com'); +console.log('First Name:', fName); + +const operations = [ + { + args: [10, 20], + params: ['a', 'b'], + body: 'console.log("a + b",a + b)', + }, + { + args: [10, 20], + params: ['a', 'b'], + body: 'console.log("a - b",a - b)', + }, + { + args: [10, 20], + params: ['a', 'b'], + body: 'console.log("a * b",a * b)', + }, + { + args: [], + params: [], + body: 'console.log("Hello World! No params, no args")', + }, + { + args: [5], + params: ['n'], + body: ` + for (let i = 0; i < n; i++) { + let line = ''; + for (let j = 0; j < n; j++) { + line += '* '; + } + console.log(line); + } + `, + }, +]; + +operations.forEach((operation) => { + const fn = new Function(...operation.params, operation.body); + fn(...operation.args); +}); + +``` + +
\ No newline at end of file diff --git a/src/lecture-08/app.js b/docs/Lectures/Fundamentals/08/app.js similarity index 100% rename from src/lecture-08/app.js rename to docs/Lectures/Fundamentals/08/app.js diff --git a/docs/Lectures/Fundamentals/09/OverView.md b/docs/Lectures/Fundamentals/09/OverView.md new file mode 100644 index 0000000..eef5bde --- /dev/null +++ b/docs/Lectures/Fundamentals/09/OverView.md @@ -0,0 +1,458 @@ +## Lecture 9 - Functional Programming in JavaScript + +## Introduction + +গত ক্লাসে আমরা ফাংশন নিয়ে আলোচনা করেছিলাম। আজ আমরা জানবো ফাংশনাল প্রোগ্রামিং নিয়ে। আজকের এজেন্ডাগুলো একটু দেখা যাক। + +- Pure Function + Side Effects + Immutability +- Higher Order Function +- Function Scope + Closure + Hoisting +- Callback +- IIFE (Immediate Invoke Function Expression) + +এই ক্লাসে রিকারশন বা কারিং নিয়ে কোনো আলোচনা হবে না। পরবর্তীতে যখন আমাদের এগুলো নিয়ে কাজ করতে হবে তখনই আমরা তা শিখে নিবো। + +ফাংশনাল প্রোগ্রামিং নিয়ে আলোচনা করতে গেলে আমাদের আগে জানতে হবে ফাংশনাল প্রোগ্রামিং কি? উইকিপিডিয়ার ভাষায়, 'In computer science, functional programming is a programming paradigm where programs are constructed by applying and composing functions.' মানে ফাংশনাল প্রোগ্রামিং এমন একটা প্যারাডাইম যেখানে প্রোগ্রাম, ফাংশন অ্যপ্লাই ও কম্পোজ করার মাধ্যমে কনস্ট্রাক্ট করা হয়। দুই ধরণের ফাংশনাল প্রোগ্রামিং ল্যাঙ্গুয়েজ আছে। Pure এবং Impure। যে প্রোগ্রামিং ল্যাঙ্গুয়েজে ফাংশনাল প্রোগ্রামিং ছাড়া অন্য কিছু সাপোর্ট করে না সেগুলোকে বলে পিওর ফাংশনাল প্রোগ্রামিং ল্যাঙ্গুয়েজ। আর যেগুলোতে ফাংশনাল প্রোগ্রামিং ছাড়াও OOP সাপোর্ট করে তাদের বলে ইমপিওর প্রোগ্রামিং ল্যাঙ্গুয়েজ। ফাংশনাল প্রোগ্রামিং এর কিছু কনসেপ্ট আছে। সেগুলো বুঝার আগে আমাদের ফাংশন কিভাবে লিখতে হয় তা একটু জানা দরকার। জাভাস্ক্রিপ্টে ফাংশন তিনভাবে লেখা যায়। ফাংশন স্টেটমেন্ট, ফাংশন এক্সপ্রেশন, ফ্যাট এরো ফাংশন (ইএস৬ ভার্সন থেকে)। এগুলোর স্ট্রাকচার একটু দেখা যাক। + +```js +// function statement +function func() {} + +// Function expression +const myFn = function () {}; + +// Fat Arrow function +const myFatArrowFn = () => {}; +``` + +এবার আমরা একটু ফাংশনাল প্রোগ্রামিং এর কনসেপ্ট সম্পর্কে জানার চেষ্টা করি। আপনি যদি [Functional Programming Languages: Concepts & Advantages](https://hackr.io/blog/functional-programming) এই লিংকে যান তাহলে দেখবেন এখানে ৫টি কনসেপ্টের কথা বলা হয়েছে। এগুলো হলো - + +- Pure Functions +- Recursion +- Referential Transparency +- Functions are First-Class and Can be Higher-Order +- Immutability + +এর মধ্যে Pure Functions, Immutability নিয়ে আজ আলোচনা হবে। Recursion নিয়ে আজ আলোচনা হবে না তা বলা হয়েছে। ফাংশন যে একটা ফার্স্ট ক্লাস সিটিজেন সেটা আমরা গত ক্লাসে দেখেছি। আজ higher order নিয়ে আলোচনা হবে। Referential Transparency বলতে বুঝাচ্ছে যদি আমরা কোনো ফাংশনকে একটা ভ্যারিয়েবলের মধ্যে স্টোর করে রাখি তাহলে তার ভ্যালু কখনও চেইঞ্জ করা যাবে না। যদিও এটা জাভাস্ক্রিপ্টের জন্য খাটে না। আমরা চাইলে ফাংশনকে ভ্যালু হিসেবে ব্যবহার করে তা চেইঞ্জ করতে পারি। বিস্তারিত জানার জন্য আপনারা [Functional Programming Languages: Concepts & Advantages](https://hackr.io/blog/functional-programming) এবং [9 Functional Programming Concepts Everyone Should Know](https://hackernoon.com/9-functional-programming-concepts-everyone-should-know-uy503u21?source=rss) আর্টিকেল দুইটি পড়তে পারেন। + +## Pure Function and side effects + +পিওর ফাংশন আর পিওর ফাংশনাল ল্যাঙ্গুয়েজ সম্পূর্ণ দুইটা আলাদা টার্ম। দুইটার মধ্যে গুলিয়ে ফেলবেন না। পিওর ফাংশন বলতে পিওর ওয়াটার ধরি আমরা। যেটা সম্পূর্ণ পিওর, যা পান করলে আমার কোনো সাইড ইফেক্ট থাকবে না। পিওর ফাংশনও তাই। যে ফাংশনের কোনো সাইড ইফেক্ট থাকবে না। যেটা আজ যে আউটপুট দিবে সেটা ১০০ বছর পরেও সেইম আউটপুট দিবে। কিন্তু আমরা তো জানি ফাংশন আমরা ব্যবহারই করি ভিন্ন ভিন্ন আউটপুট পাওয়ার জন্য। তাহলে একই আউটপুট কিভাবে সম্ভব? চলুন আমরা একটা উদাহরণ দেখি। + +```js +function sum(a, b) { + return a + b; +} + +sum(10, 20); // 30 +``` + +এই ফাংশনটা একটা পিওর ফাংশন। কিভাবে? এই ফাংশন দ্বারা কোনো কিছু চেইঞ্জ বা আপডেট করা হচ্ছে না। এখানে শুধুমাত্র এটার মধ্যে যে ভ্যারিয়েবল দেয়া হবে সেটা নিয়েই কাজ করবে। এর দ্বারা বাইরের কোনো ভ্যারিয়েবলের ভ্যালু চেইঞ্জ করা যাবে না। মানে কোনো সাইড ইফেক্ট নাই। এটা একটা দিক। আরেকটা দিক হলো যদি আর্গুমেন্ট সেইম থাকে যেমন sum এর আর্গুমেন্ট যদি ১০ এবং ২০ হয় তাহলে আজকে যেমন এর আউটপুট ৩০ আসবে, ১০০ বছর পর দিলেও এর আউটপুট সেই ৩০ আসবে। এখন একটা প্রশ্ন আসতে পারে এই সাইড ইফেক্ট জিনিসটা কি? এটা বুঝার জন্য আমরা নিচের উদাহরণটা একটু দেখি। + +```js +let limit = 100; +function changeLimit(limit) { + limit = 500; +} + +changeLimit(limit); +console.log(limit); // 100 +``` + +এটা একটা পিওর ফাংশন। কারণ এই ফাংশন বাইরের limit ভ্যারিয়েবলকে পরিবর্তন করছে না। তার মানে এর কোনো সাইড ইফেক্ট নাই। সাইড ইফেক্ট না থাকলে তাকে আমরা পিওর ফাংশন বলি। এবার ফাংশনটা অন্যভাবে লিখি একটু। + +```js +let limit = 100; +function changeLimit() { + limit = 500; +} + +changeLimit(limit); +console.log(limit); // 500 +``` + +এবার কিন্তু ফাংশনটা ভ্যারিয়েবলের ভ্যালু চেইঞ্জ করে ফেলেছে। তার মানে এটার সাইড ইফেক্ট আছে। তাই এটা একটা ইমপিওর ফাংশন। আরেকটা উদাহরণ দেখি। + +```js +const arr = [1, 2, 3]; +function add(arr, data) { + arr = [...arr, data]; + return arr; +} +``` + +এটা একটা পিওর ফাংশন। কারণ এটা মিউটেবল না, এটা নতুন অ্যারে রিটার্ন করছে। য়ামি যে অ্যারে দিয়েছি তার কোনো চেইঞ্জ সে আনছে না। তার মানে কোনো সাইড এফেক্টও নাই। এজন্য এটা একটা পিওর ফাংশন। এবার যদি আমরা এই ফাংশনকে অন্যভাবে লিখি তাহলে কেমন হবে? + +```js +const arr = [1, 2, 3]; +function add(data) { + arr.push(data); +} +``` + +এটা পুরোপুরি একটা ইমপিওর ফাংশন। কারণ তা সরাসরি arr ভ্যারিয়েবলের ডাটা আপডেট করছে। তার মানে সাইড ইফেক্ট হচ্ছে। + +এবার আপনাদের কাছে প্রশ্ন নিচের ফাংশনটা কি পিওর নাকি ইমপিওর? + +```js +function log(msg) { + console.log(msg); +} +``` + +এটা দেখতে আপাতদৃষ্টিতে পিওর ফাংশন মনে হলেও এটা একটা ইমপিওর ফাংশন। কারণ এটা কনসোলে লগ হচ্ছে। তাই যে ফাংশনে console.log() দেয়া থাকবে সেটা ইমপিওর হওয়ার পসিবিলিটি বেশি। + +## Higher order function + +Higher order function এই টার্ম যেখানে আসবে সেখানে এটার সাথে আরেকটা টার্ম যুক্ত হবে সেটা হলো ফার্স্ট ক্লাস সিটিজেন। ফার্স্ট ক্লাস সিটিজেন বলতে আমরা ফাংশনকে ভ্যালু হিসেবে ট্রিট করতে পারবো এটা বুঝায়। এটা সম্পর্কে আমরা আগে লেকচারে বুঝেছিলাম। সেটা থেকে এটা অন্তত প্রমাণিত যে জাভাস্ক্রিপ্টে ফাংশন একটা ভ্যালু। + +একটা ফাংশন হাইয়ার অর্ডার হওয়ার জন্য দুইটা শর্ত আছে। + +- Function can be passed as an argument. +- Function can be returned from another function. + +এই দুইটা শর্তের কারণে জাভাস্ক্রিপ্ট ফাংশন যে কতটা পাওয়ারফুল তা যারা এক্সপার্ট হয়ে গেছেন তারা বুঝতে পারবেন। + +ফাংশনাল প্রোগ্রামিং নিয়ে কাজ করতে হলে অবশ্যই ফাংশন পিওর হতে হবে। সবার প্রথমে আমরা প্রথমে খুব সাধারণ একটা হাইয়ার অর্ডার ফাংশন তৈরি করে ফেলি। আমরা একটা যোগ করার ফাংশন তৈরি করবো। + +```js +function sum(a, b) { + const r = a + b; + return r; +} +``` + +এটা যদিও পিওর ফাংশন, কিন্তু এটা মোটেও হাইয়ার অর্ডার ফাংশন না। কারণ এটা শর্তমতে কোনো ফাংশন রিটার্নও করছে না, আবার কোনো ফাংশনকে আর্গুমেন্ট আকারে পাসও করছে না। এখন এই ফাংশনকে হাইয়ার অর্ডার ফাংশন বানালে আমাদের কি উপকার হবে সেটা আমাদের বুঝতে হবে। আমরা কেন জোর করে একটা ফাংশনকে হাইয়ার অর্ডার ফাংশন বানাবো? ধরুন আমরা দুইটা র‍্যান্ডম নাম্বার জেনারেট করে সেই দুইটা নাম্বার দিয়ে কিছু ম্যাথমেটিকাল অপারেশন করার ফাংশন বানাবো। কিন্তু আর্গুমেন্ট আকারে আমি পাশ করবো একটা ভ্যালু। + +```js +function randomSum(max) { + const random1 = Math.floor(Math.random() * max); + const random2 = Math.floor(Math.random() * max); + return random1 + random2; +} + +function randomSub(max) { + const random1 = Math.floor(Math.random() * max); + const random2 = Math.floor(Math.random() * max); + return random1 - random2; +} + +function randomSqrSum(max) { + const random1 = Math.floor(Math.random() * max); + const random2 = Math.floor(Math.random() * max); + return random1 * random1 + random2 * random2; +} +``` + +কিন্তু এখানে দেখা যাচ্ছে র‍্যান্ডম নাম্বার জেনারেট করার অপারেশন সব ফাংশনেই একই। আমরা DRY (Don't Repeat Yourself) নীতি ফলো করি। কিন্তু এখানে তো অনেকবার রিপিট হলো। তাই আমরা চিন্তা করলাম আমরা ঐ দুই লাইনের জন্য একটা ফাংশন বানিয়ে রাখবো। + +```js +function generateTwoNumbers(max) { + const random1 = Math.floor(Math.random() * max); + const random2 = Math.floor(Math.random() * max); + + return { + random1, + random2, + }; +} +``` + +এবার আমরা আমাদের পূর্বের ফাংশনগুলিতে এই ফাংশন ব্যবহার করে আমাদের সমস্যার সমাধান করে ফেলবো। + +```js +function randomSum(max) { + const { random1, random2 } = generateTwoNumbers(max); + return random1 + random2; +} + +function randomSub(max) { + const { random1, random2 } = generateTwoNumbers(max); + return random1 - random2; +} + +function randomSqrSum(max) { + const { random1, random2 } = generateTwoNumbers(max); + return random1 * random1 + random2 * random2; +} +``` + +আমরা আমাদের প্রব্লেম সলভ করে ফেললাম। কিন্তু দুইদিন পর আমাদের ক্লায়েন্ট বললো, যে তার আরো ফাংশন দরকার, গুণ করার, ভাগ করার সহ আরো বিভিন্ন। এবার তো আবার আমাদের ফাংশন লিখতে হবে। এখন প্রথম লাইনটাও সবার জন্য সেইম। শুধু ম্যাথমেটিকাল অপারেশনটা ভিন্ন। আমরা যদি সেই ম্যাথমেটিকাল অপারেশন লেখার দায়িত্ব ইউজারের উপর ছেড়ে দিয়ে একটা ফাংশন বানিয়ে ফেলি তাহলে পরবর্তীতে যতো রিকোয়ারমেন্টই আআসুক আমাদের আর কোনো কোড রিপিট না করে সেটা বানিয়ে ফেলতে পারবো। আমরা আমাদের অপারেশন পার্টকে একটা ফাংশন হিসেবে আর্গুমেন্ট আকারে পাস করার দায়িত্ব দিয়ে রাখবো ইউজারকে। সে যা চায় সেই অপারেশনই সে করতে পারবে। সেটাকে তার হাতে দিয়ে বাকিগুলোকে নিয়ে আমরা একটা ফাংশন বানিয়ে রাখি। যার প্যারামিটার হিসেবে থাকবে max কতো আমরা দিতে চাচ্ছি, এবং একটা কলব্যাক ফাংশন যার মধ্যে সেই অপারেশনটা লেখা থাকবে। কলব্যাক ফাংশন বলতে বুঝায় আমরা একটা ফাংশনের আর্গুমেন্ট আকারে আরেকটা ফাংশন পাশ করবো। যে ফাংশনকে আর্গুমেন্ট আকারে পাস করা হচ্ছে সেটাই কলব্যাক ফাংশন। চলুন কাজটা করে দেখি। + +```js +function generateTwoRandNumber(max, cb) { + const random1 = Math.floor(Math.random() * max); + const random2 = Math.floor(Math.random() * max); + const result = cb(random1, random2); + return result; +} +``` + +এখানে আমরা সরাসরি অপারেশনটা না লিখে ইউজারকে আমাদের জেনারেট করা দুইটা নাম্বার দিয়ে দিলাম, আর বললাম তুমি তোমার মতো ফাংশন ক্রিয়েট করো, কিন্তু ফাংশনের আর্গুমেন্ট আকারে জাস্ট আমার এই নাম্বার দুইটা দিয়ে দিবে। এবার সেই ফাংশনের রেজাল্ট আমরা result ভ্যারিয়েবলের মধ্যে স্টোর করে সেটাকে রিটার্ন করে দিলাম। এবার ক্লায়েন্ট যে অপারেশনই চাইবে আমরা তা ফটাফট করে দিতে পারবো। ধরেন, আমার প্রথম রিকোয়ারমেন্ট শুধু দুইটা নাম্বার প্রিন্ট করা। + +```js +generateTwoRandNumber(100, (rand1, rand2) => console.log(rand1, rand2)); +``` + +এরপর রিকোয়ারমেন্ট আসলো ১০০০ এর মধ্যে দুইটা যেকোনো নাম্বার যোগ করার। + +```js +generateTwoRandNumber(1000, (rand1, rand2) => rand1 + rand2); +``` + +এভাবে আমরা যা খুশি সে অপারেশন করতে পারি, খুব সহজে। + +```js +generateTwoRandNumber(10, (rand1, rand2) => rand1 * rand2); +generateTwoRandNumber(10, (rand1, rand2) => rand1 * rand1 + rand2 * rand2); +``` + +যেহেতু আমাদের হাইয়ার অর্ডার ফাংশনের প্রথম শর্তমতে একটা ফাংশনকে আর্গুমেন্ট হিসেবে পাস করতে পারছি তাই এটা একটা হাইয়ার অর্ডার ফাংশন। + +এবার আসি আমাদের দ্বিতীয় শর্তে। এখন একটা ফাংশন থেকে আরেকটা ফাংশন রিটার্ন করার কি প্রয়োজন? আমরা একটু নিচের উদাহরণগুলো দেখি। ধরা যাক আমাদের একটা স্কয়ার করার ফাংশন বানাতে বলা হলো। + +```js +function sqr(a) { + return a * a; +} +``` + +এবার যদি কিউব করতে বলার হয় তাহলে আমরা কিউব করার একতা ফাংশন বানাবো। + +```js +function cube(a) { + return a * a * a; +} +``` + +কিন্তু আসল কাজ হচ্ছে এখানে পাওয়ারের কাজ। তার মানে কোনো নাম্বারের কততম পাওয়ার আমরা বের করতে চাচ্ছি মূল কাজ হচ্ছে সেটা। সুতরাং আমরা এত ফাংশন না লিখে পাওয়ারের জন্যই একটা ফাংশন বানিয়ে ফেললে হয়ে যায়। + +```js +function power(p) { + return function (n) { + let result = 1; + for (let i = 1; i <= p; i++) { + result *= n; + } + return result; + }; +} +``` + +এখানে আমরা p হিসেবে নিবো কততম পাওয়ার সেটা। আর যে ফাংশন আমরা রিটার্ন করেছি তার আর্গুমেন্ট হিসেবে নিবো কত নাম্বারের পাওয়ার বের করতে চাইছি সেটা যেটাকে n দ্বারা প্রকাশ করেছি। এবার একটু অপারেশন দেখি। + +```js +const sqr = power(2); +const cube = power(3); +const power8 = power(8); + +console.log('SQR', sqr); // SQR [Function (anonymous)] +console.log('cube', cube); // cube [Function (anonymous)] +console.log('power8', power8); // power8 [Function (anonymous)] +``` + +এখানে দেখা যাচ্ছে একটা ফাংশন রিটার্ন করছে যার আর্গুমেন্ট হিসেবে নাম্বার দিতে হবে। তাহলে আমরা নিচের কাজটা করতে পারি। যে ভ্যারিয়েবলগুলো নিয়েছি তাদের আর্গুমেন্ট হিসেবে নাম্বার দিয়ে দিলেই আমরা স্কয়ার, কিউব এবং ৮ম পাওয়ার পেয়ে যাবো। + +```js +console.log('SQR', sqr(2)); // SQR 4 +console.log('cube', cube(2)); // cube 8 +console.log('power8', power8(2)); // power8 256 +``` + +মূলত ডায়নামিক্যালি কোনো ফাংশন জেনারেট করার জন্য এবং পুরো সিস্টেমের একটা abstract layer প্রোভাইড করার জন্য আমরা একটা ফাংশন থেকে আরেকটা ফাংশন রিটার্ন করে থাকি। অর্থাৎ হাইয়ার অর্ডার ফাংশন ব্যবহার করে থাকি। + +## Hidden Concepts + +জাভাস্ক্রিপ্টের কিছু হিডেন কনসেপ্ট আছে। যেগুলো আপনি সরাসরি চোখে দেখবেন না বা কোড করার জন্য সরাসরি কোনোদিন কাজে লাগবে না। এজন্য এগুলোকে বলা হয় হিডেন কনসেপ্ট। এগুলো হলো - + +- Scope +- Closure +- Execution Context +- Hositing + +### Scope + +আমরা যদি উপরের power ফাংশনের ভিতরের ফাংশনকে বাইরে বের করে এনে দেখি তাহলে কেমন দেখা যায় দেখি। + +```js +const f = function (n) { + let result = 1; + for (let i = 1; i <= p; i++) { + result *= n; + } + return result; +}; +function power(p) { + return f; +} +``` + +এখানে একটা error throw করবে, `Reference error: p is not defined`। কারণ জাভাস্ক্রিপ্ট লেক্সিক্যাল স্কোপিং সাপোর্ট করে। মানে কোন ভ্যারিয়েবল কোথায় accessible, এটাকেই মূলত স্কোপ বলা হয়। এখন কোন ভ্যারিয়েবল বা কোন ফাংশন কোথায় এক্সেসিবল তা সেট করা হয় যখন lexing হয়। এখন lexing আবার কি? আমরা যে কোডগুলো লিখি কম্পিউটার তার কিছুই বুঝে না। কম্পিউটার বুঝে মেশিন কোড মানে বাইনারি। আমাদের লিখিত কোডকে ভেঙেচুরে মেশিন কোডে রূপান্তর করে কম্পিউটারের কাছে দিলেই কম্পিউটার তা বুঝতে পারবে। এই যে মেশিন কোডে রূপান্তরের প্রসেস, তার প্রথম ধাপই হলো parsing and lexing। অর্থাৎ আমরা যে ফাইলটা দিচ্ছি, সেটা জাভাস্ক্রিপ্ট ইঞ্জিন, অন্যান্য ল্যাঙ্গুয়েজের ক্ষেত্রে কম্পাইলার, lexing বা parsing করবে, এর মধ্যে কি কি আছে তা পড়ে ফেলবে। পড়ে সেই কোডগুলোকে টুকরো টুকরো করে ফেলবে। টুকরো টুকরো করে একটা Abstract Binary Tree (ABS) বানাবে। এই Tree বানানোর প্রসেসটাকেই বলা হচ্ছে lexing করা। আর এই lexing করার কাজটা হয় কম্পাইল করারও আগে। যেহেতু কম্পাইল করার আগে lexing হচ্ছে, তাই কাটাছেঁড়ার সময় যখন সে f ফাংশনে p পাচ্ছে না তখন সে error throw করবে। পাবে কিভাবে, সেতো কোড রান করছে না। জাস্ট পড়ছে। পড়ার সময় যদি এরর পায় সে সেটা আমাদের দিয়ে দিচ্ছে। এজন্য lexical scoping এর কিছু প্রব্লেম আছে। যদি কম্পাইল টাইমে scope সেট হতো তাহলে কম্পাইল করার সময় অনেক কিছু বুঝতে পারতো। যদি রানটাইমে scope সেট হতো তাহলে রান করার সময় আমরা ডায়নামিক্যালিভাবে scoping তৈরি করতে পারতাম। কিন্তু জাভাস্ক্রিপ্ট যেহেতু lexical scoping সাপোর্ট করে তাই এই কাজটা আমরা জাভাস্ক্রিপ্টে পারবো না। যদি f ফাংশনকে আমরা power এর মধ্যে রাখি তাহলে সে p যদি f এর কাছে না পায় তাহলে বাইরে দিয়ে দেখলে দেখবে প্যারেন্ট ফাংশনের কাছে p আছে। তাহলে আর কোনো সমস্যা নাই। + +```js +function power(p) { + const f = function (n) { + let result = 1; + for (let i = 1; i <= p; i++) { + result *= n; + } + return result; + }; + return f; +} +``` + +যদি আমরা `console.log(sqr.toString())` করি, সে একটা ফাংশন রিটার্ন করবে। এখন এই ফাংশনকে কপি করে যদি অন্য নামে আমরা স্টোর করে কল করি সে আমাদের error throw করবে। কিন্তু কেন? এই জিনিসটা লিখে বুঝানো একটু কঠিন। এটা বুঝার জন্য আপনারা ভিডিওর [1:18:21](https://youtu.be/wMy2IZ12mxM?t=4701) থেকে [1:34:06](https://youtu.be/wMy2IZ12mxM?t=5646) পর্যন্ত দেখুন। তাহলে ভাল করে বুঝবেন। + +এখন এই lexical scoping কিছু রুলস মেনে চলে। ভ্যারিয়েবল মূলত আমরা দুইভাবে লিখতে পারি। একটা হলো গ্লোবালি, আরেকটা লোকালি। এর উপর ভিত্তি করে স্কোপ প্রধানত দুই ধরণের। + +- Global +- Local + +গ্লোবাল বলতে মূলত বুঝায় আমরা কোনো ফাংশনে ভ্যারিয়েবল নিবো না, জাস্ট একটা ফাইল ক্রিয়েট করবো, করে সেখানে ভ্যারিয়েবল ডিক্লেয়ার করবো। আর লোকাল ভ্যারিয়েবল একটা ফাংশনের মধ্যে ডিক্লেয়ার করা হয়। + +```js +const a = 10; +function mostOuter() { + function outer() { + console.log(a); + } +} +``` + +এখানে lexing করার সময় যখন a পাবে, তখন সে খুঁজবে a তার ব্লকের মধ্যে আছে কিনা। না থাকলে সে বাইরের ব্লকে যাবে। সেখানেও না পেলে গ্লোবালি খুঁজবে। এভাবে lexical scoping কনসেপ্টটা কাজ করবে। এই উদাহরণে a, global ভ্যারিয়েবল হিসেবে আছে। এই স্কোপকে গ্লোবাল স্কোপ বলে। + +```js +function mostOuter() { + function outer() { + const a = 10; + console.log(a); + } +} +``` + +এখানে a আছে লোকাল ভ্যারিয়েবল হিসেবে কারণ তা একটা ফাংশনের মধ্যে আছে। এটাকে গ্লোবালি এক্সেস করা যাবে না। এই ধরণের স্কোপকে বলা হয় লোকাল স্কোপ। + +এই দুইটা ছাড়াও আরেকটা স্কোপ আছে। সেটা হলো Block scope। যেখানেই ব্লক আছে সেখানেই একটা স্কোপ তৈরি করা। ব্লক বলতে {} এর মধ্যে যা লেখা হয়। এর মধ্যে যা লেখা হবে তা বাইরে থেকে এক্সেস নেয়া পসিবল হবে না। এটাই ব্লক স্কোপ। + +```js +{ + const notScoped = 'not scoped'; +} +console.log(notScoped); // Error +``` + +আবার এই ব্লক স্কোপের মধ্যে lexing কথাটা প্রযোজ্য। যেমনঃ + +```js +{ + const notScoped = 'scoped'; + { + { + { + console.log(notScoped); // scoped + } + } + } +} +``` + +তার মানে সে তার ব্লকে `notScoped` না পেয়ে তার আউটার ব্লকে গেছে। এভাবে যেতে যেতে শেষ ব্লকে গিয়ে পেয়েছে। এটা lexing এর কনসেপ্ট। + +স্কোপ বুঝার জন্য আপনারা [গল্পে গল্পে জাভাস্ক্রিপ্ট স্কোপ](https://youtu.be/nRJPxro5GtY) ভিডিওটা দেখতে পারেন। + +### Closure + +Closure হলো একটি মেমোরি যা আমরা একটা ফাংশন নিঃশেষ হওয়ার পরে ব্যবহার করতে পারি। যেমন sqr ফাংশন কল করার পরে আমরা p এর ভ্যালু হিসেবে 2 পাই। এটা তখনই পাবো যখন ঐ ফাংশনটা পুরোপুরি শেষ হয়ে যাবে। এটাই ক্লোজার। আরো ভালভাবে বুঝতে আপনারা [গল্পে গল্পে ক্লোজার](https://youtu.be/zSlSfqQTeFE) এই ভিডিওটি দেখতে পারেন। + +### Execution Context + +আমরা একটু আগে কিছু ফাংশন বানাই। + +```js +function A(a) { + console.log('I am A'); +} + +function B() { + A(); +} + +function C() { + B(); + B(); +} +function D() { + C(); + A(); +} + +D(); +/* +I am A +I am A +I am A +*/ +``` + +কোন ফাংশনের পর কোন ফাংশন কল হবে ্তা নির্ভর করে কল স্ট্যাকের উপর। স্ট্যাক একটা ডাটা স্ট্রাকচার। এর নীতি হলো `Last In First Out (LIFO)`। মানে সবার শেষে যে আসবে সে সবার আগে বের হবে। আমরা যখন প্লেট ধুয়ে একটার উপর একটা রাখি তখন প্রথম প্লেট রাখি সবার নিচে আর শেষ প্লেট রাখি সবার উপরে। যখন আমরা প্লেট নিই তখন উপর থেকেই নিই, অর্থাৎ সবার শেষে যে প্লেটটা রেখেছিলাম সেটা নিই আগে। আর প্রথমে যেটা রেখেছিলাম সবার নিচে, সেটা নিই সবার শেষে। কল স্ট্যাক এভাবে কাজ করে। এই কোডে যখন D কল করা হলো তখন সে D এর ভিতর গিয়ে কল করবে C কে। এরপর D pause হয়ে যাবে। D এর উপর C চলে যাবে। এরপর C তে যাওয়ার পর সে কল করবে B কে। এবার B চলে যাবে C এর উপর। এবার B তে গিয়ে কল হবে A। তাহলে A চলে যাবে B এর উপর। A তে গিয়ে A কল করার পর কল স্ট্যাকের উপর থেকে A চলে যাবে। এরপর যেহেতু A কল হয়ে গেছে, সেহেতু B চলে যাবে স্ট্যাক থেকে। এরপর ফিরে আসবে C তে। C তে আসার পর দেখা গেলো B ফাংশনের কাজ আছে আরেকটা। সেটাও আগের মতো শেষ হয়ে আবার ফিরে আসবে C তে। এবার C এর কাজ শেষ। C স্ট্যাক থেকে চলে যাবে। এরপর ফিরে আসবে D তে। D তে আসার পর C এর কাজ শেষ। এবার যাবে A তে। A চলে যাবে স্ট্যাকে D এর উপর। A কল হয়ে আবার D তে ফিরে আসবে। এবার D এর কাজ শেষ। এরপর ফাইনালি স্ট্যাক থেকে D চলে যাবে। + +লেখাটা পড়ে হয়তো মাথা চক্কর দিতে পারে। ভালভাবে বোঝার জন্য ভিডিওর [2:00:33](https://youtu.be/wMy2IZ12mxM?t=7233) থেকে [2:24:39](https://youtu.be/wMy2IZ12mxM?t=8679) পর্যন্ত দেখুন। + +### Hoisting + +```js +function randomSum(max) { + const random1 = Math.floor(Math.random() * max); + const random2 = Math.floor(Math.random() * max); + t(); + function t() { + console.log(test); + } + var test = 'something'; + t(); + return random1 + random2; +} + +const r = randomSum(15); +``` + +যখন কোনো একটা ফাংশন জাভাস্ক্রিপ্ট দেখে তখন সে প্রথমে কিছু এক্সিকিউট না করে সব পড়ে নেয়। এরপর যেখানে ফাংশন পাবে সেখানে একটা রেফারেন্স তৈরি করে নেয়, আর যেখানে var পায় সেখানে undefined বসিয়ে দেয়। উপরের ফাংশনে যখন t কে একবার test ডিক্লেয়ার করার পূর্বে কল করা হয়েছে আরেকবার পরে কল করা হয়েছে। প্রথমবার আউটপুট আসবে undefined, দ্বিতীয়বার আসবে 'something'। কারণ জাভাস্ক্রিপ্ট ফাংশন প্রথমেই test এর ভ্যালু undefined বসিয়ে দিয়েছে ক্রিয়েশনাল ফেইজে। এরপর যখন এক্সিকিউশন করতে গেছে তখন প্রথমবার সেই undefined দিয়েছে এবং পরেরবার যেহেতু এক্সিকিউট হয়ে গেছে তাই 'something' দিয়েছে। এই কনসেপ্টকে বলে hoisting। একটা ভ্যারিয়েবল আমরা ডিফাইন করা পূর্বে তার ভ্যালুর এক্সেস পাচ্ছি এটাই hoisting। আমরা যদি t ফাংশনকে এভাবে না লিখে এক্সপ্রেশন আকারে লিখি + +```js +var t = function () { + console.log(test); +}; +``` + +তাহলে আমাদেরকে একটা error দিবে `TypeError: t is not a function`. কারণ তা ক্রিয়েশনাল ফেইজে var দেখার কারণে t এর ভ্যালু undefined বসিয়ে দিয়েছে। আর undefined কে তো কল করা যায় না। Point to be noted, hoisting is only applicable for var (before ES6 version), it will not work for let and const. ES6 ভার্সনে hoisting বলে কোনো টার্ম নেই। তাই আমরা ডিফাইন করার আগে যদি কল করি সেক্ষেত্রে আমাদেরকে error দিবে। + +## Callback + +কলব্যাক ফাংশন বলতে বুঝায় আমরা একটা ফাংশনের আর্গুমেন্ট আকারে আরেকটা ফাংশন পাশ করবো। যে ফাংশনকে আর্গুমেন্ট আকারে পাস করা হচ্ছে সেটাই কলব্যাক ফাংশন। উপরে generateTwoNumbers() এর উদাহরণে কলব্যাক ফাংশন দেখানো হয়েছে। + +## IIFE (Immediate Invoke Function Expression) + +আমরা যদি কোনো ফাংশন লিখে মাত্র কল করি সেটাকে বলছি Immediate Invoke Function Expression (IIFE)। যেমনঃ + +```js +(function (name) { + console.log(name); +})('Nayem'); + +(() => { + console.log('Test'); +})(); +``` + +এখন এটা আমরা এভাবে না লিখে গ্লোবালিও তো লিখতে পারতাম জাস্ট `console.log('Nayem')` এবং `console.log('test')` লিখে। এভাবে লেখার দরকার কি? আমরা যখন কোনোকিছু গ্লোবালি ডিক্লেয়ার করি তখন সেটা পাবলিকলি এক্সপোজ থাকে। যে কেউ তার এক্সেস পায়। আমি চাইছি আমার ডাটা সিকিউর রাখার জন্য। সেটা যদি একটা ফাংশনের মধ্যে রাখি সেক্ষেত্রে সেটা সিকিউর থাকে। মূলত এই কারণে আমরা IIFE ব্যবহার করে থাকি। + +## Resource for this lecture + +এই লেকচারের সমস্ত রিসোর্স [লেকচার ৯](../../../resources/lecture-09/README.md) এ পাবেন। + + + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/docs/Lectures/Fundamentals/09/Resource.md b/docs/Lectures/Fundamentals/09/Resource.md new file mode 100644 index 0000000..fb0176a --- /dev/null +++ b/docs/Lectures/Fundamentals/09/Resource.md @@ -0,0 +1,296 @@ +## Lecture 9 - Functional Programming in JavaScript + +## Today's Agenda + +- Pure Function + Side Effects + Immutability +- Higher Order Function +- Function Scope + Closure + Hoisting +- Callback Function +- IIFE (Immediate Invoke Function Expression) + +--- + +### Function statement vs Function expression + +```js +// function statement +function func() {} + +// Function expression +const myFn = function () {}; + +// Fat Arrow function +const myFatArrowFn = () => {}; +``` + +--- + +#### Pure Function and Side Effects: + +If a function is not able to change any value of a variable, the function is called pure function. If input is same, output is same for forever. For example: + +```js +// Pure Function +function sum(a, b) { + return a + b; +} + +sum(10, 20); // 30 +``` + +Now let's talk about the side effect. If a function can update the value of a variable, it's called side effect. Examples are given below: + +```js +// Pure Function +let limit = 100; +function changeLimit(limit) { + limit = 500; + return limit; +} +``` + +This will not change the value of limit. That is why This is a pure function. + +```js +// Side effect +let limit = 100; +function changeLimit() { + limit = 500; +} + +console.log(changeLimit(limit)); // undefined +console.log(limit); // 500 +``` + +This will change the value of limit. So, it is the example of side effect. There are more examples are given below: + +```jsx +// Pure Function +const arr = [1, 2, 3]; +function add(arr, data) { + arr = [...arr, data]; + return arr; +} +``` + +```js +// Side Effect +const arr = [1, 2, 3]; +function add(data) { + arr.push(data); +} +``` + +```js +// Impure Function +function log(msg) { + console.log(msg); +} +``` + +This function looks like a pure function, but it is an impure function. Because the console logs are side effects because they're logging out to the console. So if a function consists console logs there is a possibility that the function has some side effects. + +--- + +#### Higher Order Function + +There are two condition for higher order function. + +- Function can be passed as an argument. +- Function can be returned from another function. + +**Example** + +- Function can be passed as an argument. + + ```js + function randomSum(max) { + const random1 = Math.floor(Math.random() * max); + const random2 = Math.floor(Math.random() * max); + return random1 + random2; // placeholder + } + + function randomSub(max) { + const random1 = Math.floor(Math.random() * max); + const random2 = Math.floor(Math.random() * max); + return random1 - random2; // placeholder + } + + function randomSqrSum(max) { + const random1 = Math.floor(Math.random() * max); + const random2 = Math.floor(Math.random() * max); + return random1 * random1 + random2 * random2; // placeholder + } + ``` + + There are many repetitive codes in the example. To follow the DRY (Don't Repeat Yourself) we can write the functions like this: + + ```js + function generateTwoRandNumber(max, cb) { + const random1 = Math.floor(Math.random() * max); + const random2 = Math.floor(Math.random() * max); + const result = cb(random1, random2); + return result; + } + const cb = function (rand1, rand2) { + console.log(rand1, rand2); + }; + generateTwoRandNumber(100, cb); + + console.log(generateTwoRandNumber(1000, (rand1, rand2) => rand1 + rand2)); + console.log(generateTwoRandNumber(10, (rand1, rand2) => rand1 * rand2)); + console.log( + generateTwoRandNumber(10, (rand1, rand2) => rand1 * rand1 + rand2 * rand2) + ); + ``` + + Here generateTwoRandNumber() is a higher order function. Because we pass a function as an argument. + +- Function can be returned from another function. + ```js + function power(p) { + return function (n) { + let result = 1; + for (let i = 1; i <= p; i++) { + result *= n; + } + return result; + }; + } + ``` + +--- + +#### Hidden Concepts + +There are some hidden concepts: + +- Scope + + - Global + ```js + const a = 10; + function mostOuter() { + function outer() { + console.log(a); + } + } + ``` + - Local + ```js + function mostOuter() { + function outer() { + const a = 10; + console.log(a); + } + } + ``` + - Block + ```js + { + const notScoped = 'scoped'; + { + { + { + console.log(notScoped); + } + } + } + } + ``` + +- Closure: Closure is just a memory, which we can use after a function died. +- Execution context + + ```js + function A(a) { + console.log('I am A'); + } + + function B() { + A(); + } + + function C() { + B(); + B(); + } + function D() { + C(); + A(); + } + + D(); + ``` + +- Hoisting (Only applicable for var, not applicable for let and const) + + ```js + function randomSum(max) { + const random1 = Math.floor(Math.random() * max); + const random2 = Math.floor(Math.random() * max); + t(); + function t() { + console.log(test); + } + var test = 'something'; + t(); + return random1 + random2; // placeholder + } + + const r = randomSum(15); + ``` + +--- + +#### Running a code + +There are three steps to run a code + +1. Lexing/Parsing/tokenize +2. Compile +3. Run + +--- + +#### Defining a Variable + +There are two ways to define a variable. + +1. Globally +2. Locally + +--- + +### IIFE (Immediately Invoke Function Expression) + +```js +(function (name) { + console.log(name); +})('Nayem'); + +(() => { + console.log('Test'); +})(); +``` + +- Use case: + We use IIFE to protect our variable from being accessed by anyone. By IIFE we can store our confidential variable. + +--- + +#### Important Links + +- [Functional Programming Languages: Concepts & Advantages](https://hackr.io/blog/functional-programming) +- [9 Functional Programming Concepts Everyone Should Know](https://hackernoon.com/9-functional-programming-concepts-everyone-should-know-uy503u21?source=rss) +- [গল্পে গল্পে ক্লোজার](https://youtu.be/zSlSfqQTeFE) +- [গল্পে গল্পে জাভাস্ক্রিপ্ট স্কোপ](https://youtu.be/nRJPxro5GtY) +- [Source Code](../../src/lecture-09/app.js) +- [Class Overview](../../Class%20Overview/Fundamentals/Lecture-09/OverView.md) + +--- + +#### Task + +- Research about Higher Order Function. +- Create lodash library by immutable way. diff --git a/docs/Lectures/Fundamentals/09/SourceCode.md b/docs/Lectures/Fundamentals/09/SourceCode.md new file mode 100644 index 0000000..dea5871 --- /dev/null +++ b/docs/Lectures/Fundamentals/09/SourceCode.md @@ -0,0 +1,182 @@ +
+ declarativeWay.js +

This is Source Code Of object.js

+ +```javascript +// function statement +function func() {} + +// Function expression +const myFn = function () {}; + +// Fat Arrow function +const myFatArrowFn = () => {}; + +// Pure Function +function sum(a, b) { + return a + b; +} + +sum(10, 20); // 30 + +// // Side effect +// let limit = 100; +// function changeLimit() { +// limit = 500; +// } + +// console.log(changeLimit(limit)); // undefined +// console.log(limit); // 500 + +// Pure +// const arr = [1, 2, 3]; +// function add(arr, data) { +// arr = [...arr, data] +// return arr; +// } + +// Side Effect +const arr = [1, 2, 3]; +function add(data) { + arr.push(data); +} + +// Impure Function +function log(msg) { + console.log(msg); +} + +// Higher order function +function generateTwoRandNumber(max, cb) { + const random1 = Math.floor(Math.random() * max); + const random2 = Math.floor(Math.random() * max); + const result = cb(random1, random2); + return result; +} +// const cb = function (rand1, rand2) { +// console.log(rand1, rand2); +// }; +// generateTwoRandNumber(100, cb); + +// console.log(generateTwoRandNumber(1000, (rand1, rand2) => rand1 + rand2)); +// console.log(generateTwoRandNumber(10, (rand1, rand2) => rand1 * rand2)); +// console.log( +// generateTwoRandNumber(10, (rand1, rand2) => rand1 * rand1 + rand2 * rand2) +// ); + +// function randomSum(max) { +// const random1 = Math.floor(Math.random() * max); +// const random2 = Math.floor(Math.random() * max); +// return random1 + random2; // placeholder +// } + +// function randomSub(max) { +// const random1 = Math.floor(Math.random() * max); +// const random2 = Math.floor(Math.random() * max); +// return random1 - random2; // placeholder +// } + +// function randomSqrSum(max) { +// const random1 = Math.floor(Math.random() * max); +// const random2 = Math.floor(Math.random() * max); +// return random1 * random1 + random2 * random2; // placeholder +// } + +// function sqr(n) { +// return n * n; +// } + +// function cube(n) { +// return n * n * n; +// } + +function power(p) { + return function (n) { + let result = 1; + for (let i = 1; i <= p; i++) { + result *= n; + } + return result; + }; +} + +const sqr = power(2); +const cube = power(3); +const power8 = power(8); +console.log('SQR', sqr); +console.log('CUBE', cube); +console.log('Power8', power8); + +console.log(power8(2)); +console.log(power8(3)); +console.log(power8(4)); + +const a = 10; +function mostOuter() { + function outer() { + console.log(a); + } +} + +{ + const notScoped = 'scoped'; + { + { + { + console.log(notScoped); + } + } + } +} + +function A(a) { + console.log('I am A'); + if (a >= 10) { + console.log('a = ', a); + } + for (let i = 0; i < a; i++) { + console.log(i); + } +} + +function B() { + A(5); +} + +function C() { + B(); + B(); +} +function D() { + C(); + A(3); +} + +D(); + +function randomSum(max) { + const random1 = Math.floor(Math.random() * max); + const random2 = Math.floor(Math.random() * max); + t(); + function t() { + console.log(test); + } + var test = 'something'; + t(); + return random1 + random2; // placeholder +} + +const r = randomSum(15); + +(function (name) { + console.log(name); +})('Nayem'); + +(() => { + console.log('Test'); +})(); + + +``` + +
\ No newline at end of file diff --git a/src/lecture-09/app.js b/docs/Lectures/Fundamentals/09/app.js similarity index 100% rename from src/lecture-09/app.js rename to docs/Lectures/Fundamentals/09/app.js diff --git a/docs/Lectures/Fundamentals/10/Overview.md b/docs/Lectures/Fundamentals/10/Overview.md new file mode 100644 index 0000000..5599bbd --- /dev/null +++ b/docs/Lectures/Fundamentals/10/Overview.md @@ -0,0 +1,347 @@ +## Asynchronous Programming in JavaScript + +## Introduction + +আজকের লেকচার বেসিক্যালি Asynchronous Programming নিয়ে। আজকের এজেন্ডাগুলো একটু দেখে নেয়া যাক। + +- Understand Asynchronous Programming +- Event Loop +- Ways we can handle Asynchronous Tasks + - Callback + - Promise + - Async Await + - Async Iterator + - Async Generator + +একে একে সব বিষয় আলোচনা করা হবে। + + + +## Understand Asynchronous Programming + +ধরুন আপনি ব্যাংকে লাইনে দাঁড়িয়ে আছেন। সামনের জনের কাজ শেষ হলেই পরের জনের কাজ শুরু হবে। একে বলা হচ্ছে ব্লকিং। বর্তমান কাজ শেষ না হলে পরবর্তী কাজে যাওয়া যাবে না। আপনিও লাইনে দাঁড়িয়ে থাকতে থাকতে বিরক্ত হয়ে যাবেন। + +বর্তমানে কিছু কিছু ব্যাংকে এমন সার্ভিস চালু হয়েছে, আপনি ঢুকবেন, একটা টোকেন কালেক্ট করবেন, এরপর ওয়েটিং লাউঞ্জে অপেক্ষা করবেন। আপনার সিরিয়াল যখন আসবে তখন আপনাকে ডাকা হবে। আপনার আর লাইনে দাঁড়িয়ে থাকতে হলো না। প্রথম সিস্টেমে আপনি ব্যাংকে ঢুকলে আর অন্য কোনো কাজ করা সম্ভব হতো না। কিন্তু এখন আপনি টোকেন নিয়ে বসে নেট ব্রাউজিং করতে পারছেন, ল্যাপটপে প্রয়োজনীয় কাজ সারতে পারছেন, বা বাইরে থেকে কিছু ছোট কাজ সেরে আসতেও পারছেন। কারণ আপনি আপনার সিরিয়াল জানেন, আর মোটামুটি কত সময় লাগতে পারে তাও আইডিয়া করতে পারছেন। এটাকে বলা হয় নন ব্লকিং। আর যে ওয়ে সেটাকে বলা হচ্ছে Asynchronous way। + +আমরা সিনক্রোনাস প্রোগ্রামিং এর একটা উদাহরণ দিই। + +```js +console.log(1); +console.log(2); +console.log(3); +console.log(4); +console.log(5); +console.log(6); +console.log(7); +console.log(8); +console.log(9); +console.log(10); +``` + +এখানে ততক্ষণ পর্যন্ত ১০ এক্সিকিউট হবে না যতক্ষণ না ৯ পর্যন্ত এক্সিকিউট হয়। এটাকে বলে সিনক্রোনাস প্রোগ্রামিং। একটার পর একটা লাইন সিরিয়ালি এক্সিকিউট হবে। + +অ্যাসিনক্রোনাস ওয়েটা হলো, একটা প্রসেস চলাকালীন আরেকটা কাজের রিকোয়েস্ট সে নিয়ে রাখবে। যেমন ব্যাংকে কেউ ৫০০০ টাকা তুলতে যায়, কেউ ৫ কোটি টাকা। যে ৫ হাজার তুলতে যায় তার কাজ কম, যে ৫ কোটি তুলতে যায় তার কাজ বেশি। আমাদের একটা ভুল ধারণা হচ্ছে অ্যাসিনক্রোনাস ওয়েতে সময় কম লাগে, আসলে সময় যেমন লাগার তেমনই লাগে। কিন্তু আমাদের রিকোয়েস্ট ব্লক হয় না। আমরা টোকেন নেয়ার মাধ্যমে একটা রিকোয়েস্ট দিচ্ছি। এরপর তার বর্তমান কাজ শেষ হলেই পরের কাজে যাবে। ব্যাকগ্রাউন্ডে কি চলছে সেটা ইউজার বুঝতে পারে না। অ্যাসিনক্রোনাস কাজ মূলত ব্যাকএন্ডেই বেশি, ফ্রন্টএন্ডে এর কাজ নেই বললেই চলে। + +আরেকটু ভাল করে বুঝাই। ধরেন আমার একটা সার্ভার আছে। অনেকগুলো ক্লায়েন্ট রিকোয়েস্ট পাঠাবে। সার্ভারের মুখ প্রথমে ওপেন আছে। যেই একটা ক্লায়েন্ট থেকে রিকোয়েস্ট আসলো অমনি সার্ভারের মুখ ক্লোজ হয়ে গেলো। সার্ভারে সেই কাজটা সম্পন্ন হতে ধরেন ৫ সেকেন্ড লাগলো। ঐ ৫ সেকেন্ড সার্ভারের মুখ বন্ধ থাকবে। আর কোনো ক্লায়েন্ট থেকে রিকোয়েস্ট সেখানে ঢুকতে পারে না। আমরা রেজাল্ট দেখার সময় এই সমস্যায় পড়ি। রিফ্রেশ করেই যাই কিন্তু ঢুকা আর যায় না। কারণ একজনের রিকোয়েস্ট হ্যান্ডেল না হলে অন্যজনের রিকোয়েস্ট ঢুকতে পারছে না। একে বলে ব্লকিং। + +আর নন ব্লকিং এর ক্ষেত্রে সার্ভার রিকোয়েস্ট ব্লক না করে একটা কিউ (Queue) তে রেখে দেয়। যত রিকোয়েস্ট আসবে সব গিয়ে কিউতে জমা হবে। এরপর সিরিয়াল ধরে ঐ কিউ থেকে রেসপন্স যার যার কাছে যাবে। এক্ষেত্রে কোনো রিকোয়েস্ট ব্লক হচ্ছে না। একে বলে নন ব্লকিং আর ওয়েটা হলো অ্যাসিনক্রোনাস ওয়ে। ছোট একটা কনসেপ্ট অনেক বড় বিপ্লব নিয়ে এসেছে প্রোগ্রামিং জগতে। + +ল্যাঙ্গুয়েজ কখনও সিনক্রোনাস, অ্যাসিনক্রোনাস হতে পারে না। এই ফিচারটা থাকে ঐ ল্যাঙ্গুয়েজের যে কম্পাইলার বা রানটাইম থাকে সেখানে। জাভাস্ক্রিপ্টের ক্ষেত্রে v8 ইঞ্জিন হলো অ্যসিনক্রোনাস। + +অ্যাসিনক্রোনাস টাস্কের একটা উদাহরণ দেখি আমরা। + +```js +console.log(1); + +setTimeout(() => { + console.log(2); +}, 0); + +setTimeout(() => { + console.log(3); +}, 0); + +setTimeout(() => { + console.log(4); +}, 0); + +setTimeout(() => { + console.log(5); +}, 0); + +setTimeout(() => { + console.log(6); +}, 0); + +setTimeout(() => { + console.log(7); +}, 0); + +console.log(8); +``` + +যদিও এখানে টাইম দেয়া আছে ০, মানে কোনো অপেক্ষা করবে না, তাও `setTimeout` থাকলেই সেই টাস্ক কিউতে যাবে। এবং অ্যাসিনক্রোনাস ওয়েতে কাজ করবে। তাহলে প্রথমে এক্সিকিউট হবে `1`, এরপর `8`, এরপর একে একে `2`, `3`, `4`, `5`, `6`, `7`। + +আরেকটু ভালভাবে বুঝার জন্য আর একটা এক্সাম্পল দেখি। + +```js +function main() { + setTimeout(() => { + console.log('load last'); + }, 10); + + setTimeout(() => { + console.log('load first'); + test(); + }, 0); + + test(); +} + +function test() { + console.log('test'); +} + +main(); +``` + +এই কোডটা ভালভাবে ভিজ্যুয়ালাইজ করার জন্য আপনারা [JavaScript Visualizer 9000](https://www.jsv9000.app/) এ গিয়ে রান করতে পারেন। + +এখানে প্রথমে `main` ফাংশন কল স্ট্যাকে যাবে। এরপর main ফাংশনে যাওয়ার পর দেখবে দুইটা অ্যাসিনক্রোনাস টাস্ক আছে। সেই দুইটা চলে যাবে টাস্ক কিউতে। এরপর যাবে test এর কাছে। test চলে যাবে কল স্ট্যাকে। সেখানে থেকে test এক্সিকিউট হবে। test কল স্ট্যাক থেকে বের হয়ে যাবে। এরই সাথে main এরও কাজ শেষ, সেও কল স্ট্যাক থেকে বের হয়ে যাবে। এখন কল স্ট্যাক কিউ থেকে অ্যাসিনক্রোনাস টাস্কগুলোকে নিয়ে আসবে। প্রথমে আনবে যার এক্সিকিউশন টাইম কম তাকে। এক্ষেত্রে ০ এক্সিকিউশন টাইমের টাস্ককে কল স্ট্যাক নিয়ে আসবে। `load first` প্রিন্ট হবে। এরপর সেখানে test ফাংশন পাওয়ার পর test কল স্ট্যাকে আসবে। test রান হবে। কল স্ট্যাক থেকে চলে যাবে। এরপর ১০ মিলিসেকেন্ডের টাস্ক কিউ থেকে কল স্ট্যাকে আসবে। সেটা এক্সিকিউট হবে। + +তার মানে দেখা যাচ্ছে, সমস্ত সিনক্রোনাস টাস্ক প্রথমে কল স্ট্যাকে যাবে এবং সমস্ত অ্যাসিনক্রোনাস টাস্ক কিউতে যাবে। সিনক্রোনাস টাস্ক শেষ হওয়ার পর কল স্ট্যাক খালি হলে, কিউ থেকে কম এক্সিকিউশন টাইমের কাজ কল স্ট্যাকে আসবে এবং এক্সিকিউট হবে। এটাই অ্যাসিনক্রোনাস টাস্কের কনসেপ্ট। আপনারা কোডটা কপি করে উপরের সাইটে গিয়ে দেখলে আরো ক্লিয়ারলি বুঝতে পারবেন। + +একটা জিনিস মাথায় রাখতে হবে অ্যাসিনক্রোনাস টাস্কের ভ্যালু আপনি কখনও ভ্যারিয়েবলে অ্যাসাইন করতে পারবেন না। + +অ্যাসিনক্রোনাস প্রোগ্রামিং সম্পর্কে আরো জানতে [Asynchronous JavaScript - Learn web development | MDN](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous) এই আর্টিকেলটা পড়ুন। + +## Event Loop + +Event loop হলো, আমরা প্রথমে কোনো রিকোয়েস্ট কিউতে রেখে দিই, এরপর কল স্ট্যাক খালি হলে একটার পর একটা কিউ থেকে কল স্ট্যাকে পাঠাই। এই যে কিউ থেকে কল স্ট্যাকে পাঠানো এটা একটা লুপের মতো কাজ করে। আর এটাই ইভেন্ট লুপ। নিচের চিত্রটা দেখলে আরো পরিষ্কার হবে। + +![event-loop](./event-loop.gif) + +ইভেন্ট লুপ নিয়ে আরো জানতে [The event loop - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop), [The JavaScript Event Loop: Explained - Towards Dev](https://towardsdev.com/event-loop-in-javascript-672c07618dc9), [What the heck is the event loop anyway? | Philip Roberts | JSConf EU](https://youtu.be/8aGhZQkoFbQ) এই আর্টিকেলগুলো পড়তে পারেন। + +## Ways we can handle Asynchronous Tasks + +অ্যসিনক্রোনাস নিয়ে কাজ করতে গেলে আমাদের দুইটা প্রশ্নের উত্তর লাগবে। + +- কখন আমাদের এই কোড এক্সিকিউট হবে? +- কোড এক্সিকিউট হওয়ার পর যে ডাটাগুলো পাবো সেগুলো আমরা কিভাবে হ্যান্ডেল করবো? + +প্রথম প্রশ্নের উত্তর আমরা পেয়ে গেছি অলরেডি। দ্বিতীয় প্রশ্নের উত্তর নিচে আলোচনা করা হলোঃ + +### Callback + +অ্যাসিনক্রোনাস টাস্ক হ্যান্ডেল করার জন্য একটা উপায় হলো কলব্যাক। কিন্তু কলব্যাকের একটা সমস্যা আছে। যেটার নাম কলব্যাক হেল। মানে একটা কলব্যাকের ভিতর আরেকটা কলব্যাক, সেটার ভিতর আরেকটা, সেটার ভিতর আরেকটা এভাবে করে চলতেই থাকবে যতক্ষণ না পর্যন্ত আপনি লাস্ট ডাটাটা পাচ্ছেন। এটা একটা বড় সমস্যা। সবচেয়ে বড় সমস্যা কোড লেখাও না, কোড পড়াও না, সবচেয়ে বড় সমস্যা হলো কোড ডিবাগ করা। আবার যেহেতু আমি প্রথম কলব্যাকের রেজাল্টটা কোথাও স্টোর করে রাখতে পারছি না তাই তার রেজাল্ট পাওয়ার জন্য আরেকটা কলব্যাক ব্যবহার করতেই হচ্ছে। তাই কলব্যাক অ্যাসিনক্রোনাস টাস্ক হ্যান্ডেল করার একটা সহজ উপায় হলেও আমরা কলব্যাক ব্যবহার করবো না। + +ধরেন আমাদের একটা টাস্ক দেয়া হলো। এর ডিটেলস নিচে দেয়া হলোঃ + +```js +/** + * 1. find user by username + * 2. find post by userId + * 3. find latest post + * 4. find comments by post id + * 5. find latest comment + * 6. find username of the latest commented user + */ +``` + +আমাদের এমন কোনো API নাই যেখানে গিয়ে আমরা ইউজার নেইম দিয়ে দিবো আর সেই অনুসারে কমেন্টেড ইউজারের নাম দেখাবে। আমার সিস্টেমে অনেকগুলো ডিফারেন্ট API আছে। এই API গুলো থেকে খুঁজে আনতে হবে আমাদের। তো আমরা আমাদের API Endpoint গুলো লিখে ফেলি। + +```js +/** + * /users?username=[username] + * /posts?user_id=[user_id] + * /comments?post_id=[post_id] + * /users?username=[username] + */ +``` + +প্রথমে আমাদের username বের করে আনতে হবে। এখন username পেলে আমরা userid ও পাবো। সেটা দিয়ে latest post বের করে আনতে পারবো। এবার পোস্ট পেলে পোস্ট আইডি পাবো। সেই আইডি দিয়ে কমেন্ট বের করে আনতে পারবো। কমেন্ট থেকে ইউজার নেইম পাবো। এবার আমাদের প্রথম Endpoint এ আবার হিট করতে হবে। তার মানে মোট ৪টা অ্যাসিনক্রোনাস টাস্ক আছে এখানে। কিভাবে বুঝলাম আমরা এগুলো অ্যাসিনক্রোনাস টাস্ক। কারণ আমরা একটা সার্ভার থেকে আরেকটা সার্ভারে কমিউনিকেশন করছি। এক সার্ভার থেকে আরেক সার্ভারে কমিউনিকেশন করা অ্যাসিনক্রোনাস টাস্ক। এছাড়াও, setTimeout, setInterval, ফাইল রীড করা এসবও অ্যাসিনক্রোনাস টাস্ক। + +```js +function get(path, cb) { + const data = {}; // somehow process it + cb(data); +} + +function getUserNameFromComment(username) { + get(`users?username=${username}`, (user) => { + get(`posts?user_id=${user.id}`, (posts) => { + get(`comments?post_id=${posts[0].id}`, (comments) => { + get(`users?username=${comments[0].username}`, (user) => { + console.log(user); + }); + }); + }); + }); +} + +getUserNameFromComment('arif'); +``` + +প্রথমে আমরা একটা ফাংশন নিলাম আমাদের ইউজার পাওয়ার জন্য। যেহেতু আমরা আগেই বলেছি অ্যাসিনক্রোনাস টাস্কের রেজাল্ট কোনো ভ্যারিয়েবলে স্টোর করা যায় না তাই আমাদের ডাটা পাওয়ার জন্য লাগবে একটা কলব্যাক ফাংশন। এবার আমরা আরেকটা ফাংশন বানালাম। get এর path হিসেবে দিলা আমাদের ইউজারনেম পাওয়ার এন্ডপয়েন্ট এবং আরেকটা কলব্যাক ফাংশন পোস্ট পাওয়ার জন্য। পোস্ট পাওয়ার পর আরেকটা ফাংশন বানালাম লেটেস্ট পোস্টের আইডি পাওয়ার জন্য। এরপর আবার আরেকটা ফাংশন বানালাম লেটেস্ট কমেন্ট কে করেছে তার নাম পাওয়ার জন্য। যেহেতু সেই নাম কোনো ভ্যারিয়েবলে স্টোর করতে পারবো না, তাই সেই নামটা প্রিন্ট করার জন্য আমাদের আরেকটা কলব্যাক ফাংশন বানাতে হবে। এখন চিন্তা করেন এখানে মাত্র ৪টা টাস্ক। যদি ১০০টা হয়, ১০০০টা হয় তখন আপনি কিভাবে ডিবাগ করবেন? আপনি পাগল হয়ে যাবেন। তাই কখনও আমরা এই কলব্যাক ব্যবহার করবো না। তাহলে এর চেয়ে বেটার সল্যুশন কি? চলুন দেখি। + +### Promise + +Promise হলো জাভাস্ক্রিপ্টের একটা অবজেক্ট যার ভ্যালু ইনিশিয়ালি থাকবে না, কিন্তু ভবিষ্যতে আসবে। এটা resolve হতেও পারে নাও হতে পারে। এখন প্রমিজ কিভাবে ক্রিয়েট করা যায়? যেহেতু আমরা বলেছি জাভাস্ক্রিপ্টে প্রমিজ একটা অবজেক্ট সুতরাং সকল অবজেক্টের মতোই এর তৈরি করার সিনট্যাক্স একই `new Promise()`। এই Promise এর মধ্যে একটা কলব্যাক ফাংশন থাকবে যার প্যারামিটার হিসেবে দুইটা জিনিস নিবো। resolve and reject. সাধারণত প্রমিজ বানানো হয় হয় রাখার জন্য নাহয় ভাঙার জন্য। রাখার জন্য হলে resolve আর ভাঙার জন্য হলে reject। + +```js +const isResolved = true; + +const promise = new Promise((resolve, reject) => { + if (isResolved) { + resolve('completed'); + } else { + reject('data'); + } +}); + +console.log(promise); // Promise { 'completed' } +``` + +`isResolved = true` হলে এরকম আউটপুট আসবে। কিন্তু যদি false হয় সে অনেক বড়সড় একটা এরর দেখাবে। এই এরর থেকে বাঁচতে আমরা `catch` ব্লক ব্যবহার করতে পারি। প্রমিজের তিনটা ফাংশন থাকে। এগুলো হলোঃ + +- then: যখন প্রমিজ resolve হয়ে যাবে তখন then ব্লক কল করবে। +- catch: যখন প্রমিজ কিছু resolve করতে পারবে না অর্থাৎ reject হবে তখন সেটা একটা এরর। সেই কাজটা হ্যান্ডেল করবে catch ব্লক। +- finally: প্রমিজ resolve বা reject যাই হোক না কেন লাস্ট একটা ব্লক কল করবেই, সেটা হলো finally। + +```js +promise + .then((result) => { + console.log(result); + }) + .catch((e) => { + console.log('Rejected'); + }); +``` + +এভাবে ছাড়াও `Promise.resolve()`, `Promise.reject()` এভাবেও করা যায় সরাসরি। এখন প্রশ্ন আসতে পারে যদি সরাসরিই করা যায় আমাদের প্রমিজ বানানোর দরকার কি? কিছু কিছু API, functions আছে যারা আর্গুমেন্ট আকারে শুধু প্রমিজই নিবে, অন্য কিছু নিবে না। সেক্ষেত্রে আমাদের ডাটাকে প্রমিজ বানিয়ে ফেলতে হবে। প্রমিজ কিভাবে বানানো যায়। তা নিচের ছবিতে দেখুন। + +![Promise](./Screenshot_1.png) + +Promise বানানোর পর যেভাবে আমরা প্রমিজের কাজগুলো করতে পারতাম তার সবই করতে পারবো। চলুন একটু দেখি। + +![Promise2](./Screenshot_2.png) + +আমরা একটা ছোটখাট টাইমার টাইপের অ্যাপলিকেশন বানানোর চেষ্টা করি। + +```js +const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + +wait(1000).then(() => { + console.log('Done in 1000ms'); +}); + +wait(2000).then(() => { + console.log('Done in 2000ms'); +}); + +wait(3000).then(() => { + console.log('Done in 3000ms'); +}); +``` + +এক সেকেন্ড পরপর তিনটা এক্সিকিউট হবে। + +এবার আমরা কলব্যাকে যে রিকোয়ারমেন্টস নিয়ে কাজ করেছিলাম সেটা আমরা প্রমিজ দিয়ে কিভাবে করতে পারি সেটা দেখা যাক। + +যদি একটা টাস্ক আরেকটা টাস্কের উপর ডিপেন্ডেন্ট হয় তাহলে প্রমিজের ক্ষেত্রে আমরা একটা চেইন ক্রিয়েট করতে পারি। কলব্যাকে যেভাবে একটার ভিতর আরেকটা কলব্যাক ব্যবহার করেছিলাম এখানে আমরা চেইন ক্রিয়েট করবো। ধরি আমাদের কাছে একটা ফাংশন আছে যেটা প্রমিজ রিটার্ন করে। যদি প্রমিজ রিটার্ন করে তাহলে আমরা then ফাংশনে যেতে পারবো, নাহয় যেতে পারব না। + +```js +const get = (url) => Promise.resolve(url); + +get(`/users?username=anarul`) + .then((user) => { + /** do all other operations here */ + return get(`/posts?user_id=${user.id}`); + }) + .then((posts) => { + const latestPost = posts[0]; + return get(`/comments?post_id=${latestPost.id}`); + }) + .then((comments) => { + const latestComment = comments[0]; + return get(`/users?username=${latestComment.username}`); + }) + .then((user) => { + console.log(user); + }) + .catch(() => { + console.log('Error'); + }); +``` + +এখানেও অনেক কাজ করতে হয়েছে। তবে কলব্যাকের তুলনায় এই কোডটা অনেক ভালভাবে পড়া যাচ্ছে। চিন্তা করেন কলব্যাকে প্রতিটা ফাংশনের জন্য যদি try catch ব্লক ব্যবহার করি তাহলে কতটা কষ্ট হবে আমাদের। সেখানে প্রমিজে আমরা মাত্র একটা catch ব্লক ব্যবহার করে আমাদের সমস্ত এরর হ্যান্ডেল করতে পারি। আর চেইন আকার হওয়ায় আমরা কোডটা সহজেই বুঝতে পারছি। + +কিন্তু তাও এটাও অনেক কষ্টকর। খুব বেশি যে সহজ হয়ে গেলো তা না। আরো সহজ সল্যুশন আছে এটার চাইতে। চলুন দেখা যাক। + +### Async Await + +Async Await এর ক্ষেত্রে প্রমিজ থাকলে সেটা then catch কিছু লেখার দরকার নাই। সরাসরি রেজাল্ট নিয়ে আসতে পারি। এর একটা শর্ত হচ্ছে async ফাংশন না হলে আমরা await করতে দিবো না। await এর মানেই হচ্ছে অপেক্ষা করা। Async Await হচ্ছে অনেকটা অ্যাসিনক্রোনাস প্রোগ্রামিং এর সিনক্রোনাস সিনট্যাক্স। দেখতে মনে হবে সিনক্রোনাস, কিন্তু কাজ হবে অ্যাসিনক্রোনাসের। কোনো ফাংশনকে async বানাতে হলে function কীওয়ার্ডের আগে জাস্ট async বসিয়ে দিলেই তা async ফাংশন হয়ে যাবে। এখন এই ফাংশন কিছু করুক বা না করুক একটা প্রমিজ রিটার্ন করবে। বিশ্বাস করার জন্য তো প্রমাণ দরকার। চলুন একটা প্রমাণ দেখাই। + +![Async](./Screenshot_3.png) + +যখন আমরা async কীওয়ার্ড ইউজ করিনি, তখন ফাংশন আমাদের undefined রিটার্ন করছে। কিন্তু যখন async ফাংশন লিখলাম তা আমাদের একটা প্রমিজ রিটার্ন করছে। + +এবার আমরা আমাদের আগের টাস্ক Async Await দিয়ে করি। + +```js +const get = (url) => Promise.resolve(url); + +async function getUserName(username) { + try { + const mainUser = await get(`/users?username=${username}`); + const posts = await get(`/posts?user_id=${mainUser.id}`); + const comments = await get(`/comments?post_id=${posts[0].id}`); + const user = await get(`/users?username=${comments[0].username}`); + console.log(user); + } catch (e) { + console.log(e); + } +} +``` + +যখনই ডাটা আসার ব্যাপার থাকবে তখন তা আসার জন্য কিছু সময় লাগবে। ঐ সময়টা যেন ব্লক হয়ে না থাকে তাই await দিয়ে বুঝানো হয় তোমার রিকোয়েস্ট প্রসেসিং হচ্ছে, একটু টাইম লাগবে। তুমি অপেক্ষা করো। যে ডাটা আসছে তা আমরা একেকটা ভ্যারিয়েবলে স্টোর করছি। সবশেষে ইউজার আসার পর আমরা তা প্রিন্ট করবো। এখানে দেখুন একটা try catch ব্লক দিয়ে কাজটা শেষ হয়ে যাচ্ছে। কোনো চেইন নেই। যেহেতু ভ্যারিয়েবলে আমরা ডাটা স্টোর করে রাখতে পারছি, আমরা ভ্যারিয়েবল ধরে ধরে ডিবাগ করতে পারি। খুব সহজেই পড়া যাচ্ছে। প্রমিজ, কলব্যাকে যেটা অনেক কাজ করতে হতো, এক্ষেত্রে অনেক কম কাজ করে অ্যাসিনক্রোনাস টাস্ক হ্যান্ডেল করা যায়। + +এবার আমরা একটা রিয়েল লাইফ উদাহরণ দেখি। তার জন্য আমাদের axios প্যাকেজ ইনস্টল করে নিতে হবে এবং [jsonPlacehlder](https://jsonplaceholder.typicode.com) থেকে ডাটা নিতে পারি। + +```js +const axios = require('axios').default; +const USERS_URL = 'https://jsonplaceholder.typicode.com/users'; +const POSTS_URL = 'https://jsonplaceholder.typicode.com/posts'; +const COMMENTS_URL = 'https://jsonplaceholder.typicode.com/comments'; + +async function getComments(username) { + try { + const { data: user } = await axios.get(`${USERS_URL}?username=${username}`); + const { data: posts } = await axios.get( + `${POSTS_URL}?userId=${user[0].id}` + ); + const { data: comments } = await axios.get( + `${COMMENTS_URL}?postId=${posts[0].id}` + ); + + const { data: userWithComment } = await axios.get( + `${USERS_URL}?email=${comments[1].email}` + ); + console.log(userWithComment); + } catch (error) { + console.log('Error Occurred', error.toJSON()); + } +} + +getComments('Bret'); +``` + +প্রথমে আমরা ইউজার, পোস্ট এবং কমেন্টের URL কে ভ্যারিয়েবলে নিয়ে নিলাম। এরপর async ফাংশন বানালাম। সর্বপ্রথমে আমরা ইউজারনেইম দিয়ে ইউজার বের করে স্টোর করলাম। এরপর ঐ ইউজারের আইডি ব্যবহার করে সকল পোস্ট বের করে নিলাম। এরপর ঐ পোস্টগুলোর মধ্য থেকে প্রথম পোস্টের আইডি ব্যবহার করে কমেন্টগুলো বের করে নিলাম। তারপর প্রথম কমেন্টের থেকে আমরা ইউজার ইমেইল বের করে নিলাম। এরপর ঐ ইমেইল দিয়ে ইউজার বের করার জন্য হিট করলাম। কিন্তু কোনো ইউজার পাওয়া না যাওয়ার তা একটা ফাঁকা অ্যারে [] আউটপুট দিচ্ছে। এরর হ্যান্ডলিং এর জন্য try catch ব্লক ব্যবহার করেছি। + +Async Iterator এবং Async Generator নিয়ে নেক্সট ক্লাসে আলোচনা করা হবে। + + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/docs/Lectures/Fundamentals/10/Resource.md b/docs/Lectures/Fundamentals/10/Resource.md new file mode 100644 index 0000000..f070311 --- /dev/null +++ b/docs/Lectures/Fundamentals/10/Resource.md @@ -0,0 +1,299 @@ +## Lecture 10 - Asynchronous Programming in JavaScript + +## Agenda + +- Understand Asynchronous Programming +- Event Loop +- Ways we can handle Asynchronous Tasks +- Callback +- Promise +- Async Await + +--- + +### Understand Asynchronous Programming + +Suppose you are standing on a line in a bank. The process is when a task is completed, then another task will start. Next task will not be started before current task has completed. This is called blocking. + +But nowadays, many banks are adapted a new way. You enter to the bank, collect a token. Then you can wait in lounge, browse internet or you can finish some small task in outside. When you number will come, it is announced. Then you can go to the counter. This is called non-blocking. And the way is called asynchronous way. + +setTimeOut and setInterval are the example of asynchronous programming in javascript. Let's look at below example. + +```js +console.log(1); + +setTimeout(() => { + console.log(2); +}, 0); + +setTimeout(() => { + console.log(3); +}, 0); + +setTimeout(() => { + console.log(4); +}, 0); + +setTimeout(() => { + console.log(5); +}, 0); + +setTimeout(() => { + console.log(6); +}, 0); + +setTimeout(() => { + console.log(7); +}, 0); + +console.log(8); +``` + +At first 1 will be printed, then 8 will be printed. All the `setTimeout` tasks will wait in Queue. After printing of 8, first `setTimeout` task with 2 will enter in call stack. Then it will print. After printing, 3, 4, 5, 6, 7 will be printed. + +Let's look into another example: + +```js +function main() { + setTimeout(() => { + console.log('load last'); + }, 10); + + setTimeout(() => { + console.log('load first'); + test(); + }, 0); + + test(); +} + +function test() { + console.log('test'); +} + +main(); +``` + +At first, `main()` will enter in call stack. Then `setTimeout` task with 10 ms time will enter in Queue. Second `setTimeout` task will also enter in Queue. Then `test()` function will enter in call stack and executed first. With execution of `test()` function `main()` function will leave from call stack. Then the `setTimeout` task with lower time i.e. second `setTimeout` task will enter in the call stack and executed first. At last first `setTimeout` task will enter in call stack and executed finally. + +You can visually see the process in [JavaScript Visualizer 9000](https://www.jsv9000.app/). + +> We can't store value of an asynchronous task into a variable. + +To learn more please go though [Asynchronous JavaScript - Learn web development | MDN](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous). + +--- + +#### Event Loop + +Event Loop is just a loop, which transfer data from the queue to call stack. +![event-loop](./event-loop.gif) +To learn more go through [The event loop - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop), [The JavaScript Event Loop: Explained - Towards Dev](https://towardsdev.com/event-loop-in-javascript-672c07618dc9) and [What the heck is the event loop anyway? | Philip Roberts | JSConf EU](https://youtu.be/8aGhZQkoFbQ) + +--- + +#### Ways we can handle Asynchronous Tasks + +- ##### Callback + + Though Callback is a way to handle Asynchronous tasks, we will not use callback ever. The main reason is callback hell. Look into the example of callback hell in below: + + ```js + /** + * 1. find user by username + * 2. find post by userId + * 3. find latest post + * 4. find comments by post id + * 5. find latest comment + * 6. find username of the latest commented user + */ + + /** + * /users?username=[username] + * /posts?user_id=[user_id] + * /comments?post_id=[post_id] + * /users?username=[username] + */ + + function get(path, cb) { + const data = {}; // somehow process it + cb(data); + } + + function getUserNameFromComment(username) { + get(`users?username=${username}`, (user) => { + get(`posts?user_id=${user.id}`, (posts) => { + get(`comments?post_id=${posts[0].id}`, (comments) => { + get(`users?username=${comments[0].username}`, (user) => { + console.log(user); + }); + }); + }); + }); + } + + getUserNameFromComment('arif'); + ``` + + The main problem of callback is debugging. We can't debug easily. And because we can't store the data from first callback in any variable, we need to use another callback. So, it is very difficult to work with the callback. That is why, we don't use callback. + +- ##### Promise + + `Promise` is a JavaScript Object. Initially `Promise` doesn't have any value. But, in future a value will come. Look into the example given below: + + ```js + const isResolved = true; + + const promise = new Promise((resolve, reject) => { + if (isResolved) { + resolve('completed'); + } else { + reject('data'); + } + }); + + console.log(promise); + + promise + .then((result) => { + console.log(result); + }) + .catch((e) => { + console.log('Rejected'); + }); + ``` + + Let's see another example: + + ```js + const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + + wait(1000).then(() => { + console.log('Done in 1000ms'); + }); + + wait(2000).then(() => { + console.log('Done in 2000ms'); + }); + + wait(3000).then(() => { + console.log('Done in 3000ms'); + }); + ``` + + Now let's implement the problem solved by callback previously. + + ```js + /** + * 1. find user by username + * 2. find post by userId + * 3. find latest post + * 4. find comments by post id + * 5. find latest comment + * 6. find username of the latest commented user + */ + + /** + * /users?username=[username] + * /posts?user_id=[user_id] + * /comments?post_id=[post_id] + * /users?username=[username] + */ + + const get = (url) => Promise.resolve(url); + + get(`/users?username=anarul`) + .then((user) => { + /** do all other operations here */ + return get(`/posts?user_id=${user.id}`); + }) + .then((posts) => { + const latestPost = posts[0]; + return get(`/comments?post_id=${latestPost.id}`); + }) + .then((comments) => { + const latestComment = comments[0]; + return get(`/users?username=${latestComment.username}`); + }) + .then((user) => { + console.log(user); + }) + .catch(() => { + console.log('Error'); + }); + ``` + +- ##### Async Await + + Normally, a function with nothing inside it returns `undefined`. But an asynchronous function by default returns a `Promise`. Let's look into the previous example: + + ```js + const get = (url) => Promise.resolve(url); + + async function getUserName(username) { + try { + const mainUser = await get(`/users?username=${username}`); + const posts = await get(`/posts?user_id=${mainUser.id}`); + const comments = await get(`/comments?post_id=${posts[0].id}`); + const user = await get(`/users?username=${comments[0].username}`); + console.log(user); + } catch (e) { + console.log(e); + } + } + ``` + + **Real Example** + + ```js + const axios = require('axios').default; + const USERS_URL = 'https://jsonplaceholder.typicode.com/users'; + const POSTS_URL = 'https://jsonplaceholder.typicode.com/posts'; + const COMMENTS_URL = 'https://jsonplaceholder.typicode.com/comments'; + + async function getComments(username) { + try { + const { data: user } = await axios.get( + `${USERS_URL}?username=${username}` + ); + const { data: posts } = await axios.get( + `${POSTS_URL}?userId=${user[0].id}` + ); + const { data: comments } = await axios.get( + `${COMMENTS_URL}?postId=${posts[0].id}` + ); + + const { data: userWithComment } = await axios.get( + `${USERS_URL}?email=${comments[1].email}` + ); + console.log(userWithComment); + } catch (error) { + console.log('Error Occurred', error.toJSON()); + } + } + + getComments('Bret'); + ``` + +--- + +#### References + +- [The event loop - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop) +- [The JavaScript Event Loop: Explained - Towards Dev](https://towardsdev.com/event-loop-in-javascript-672c07618dc9) +- [What the heck is the event loop anyway? | Philip Roberts | JSConf EU](https://youtu.be/8aGhZQkoFbQ) +- [Asynchronous JavaScript - Learn web development | MDN](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous) +- [JavaScript Visualizer 9000](https://www.jsv9000.app/) +- [Class Overview](../../Class%20Overview/Lecture-10/README.md) + +--- + +#### Source code: + +[Lecture 10 - Asynchronous Programming in JavaScript - Source Code](../../src/lecture-10/app.js) + +--- + +#### Tasks: + +- Create [Lodash](https://lodash.com) library on your own. diff --git a/class-overview/Lecture-10/Screenshot_1.png b/docs/Lectures/Fundamentals/10/Screenshot_1.png similarity index 100% rename from class-overview/Lecture-10/Screenshot_1.png rename to docs/Lectures/Fundamentals/10/Screenshot_1.png diff --git a/class-overview/Lecture-10/Screenshot_2.png b/docs/Lectures/Fundamentals/10/Screenshot_2.png similarity index 100% rename from class-overview/Lecture-10/Screenshot_2.png rename to docs/Lectures/Fundamentals/10/Screenshot_2.png diff --git a/class-overview/Lecture-10/Screenshot_3.png b/docs/Lectures/Fundamentals/10/Screenshot_3.png similarity index 100% rename from class-overview/Lecture-10/Screenshot_3.png rename to docs/Lectures/Fundamentals/10/Screenshot_3.png diff --git a/docs/Lectures/Fundamentals/10/SourceCode.md b/docs/Lectures/Fundamentals/10/SourceCode.md new file mode 100644 index 0000000..75eb02b --- /dev/null +++ b/docs/Lectures/Fundamentals/10/SourceCode.md @@ -0,0 +1,224 @@ +
+ app.js +

This is Source Code Of app.js

+ + + ```javascript +// console.log(1); + +// setTimeout(() => { +// console.log(2); +// }, 0); + +// setTimeout(() => { +// console.log(3); +// }, 0); + +// setTimeout(() => { +// console.log(4); +// }, 0); + +// setTimeout(() => { +// console.log(5); +// }, 0); + +// setTimeout(() => { +// console.log(6); +// }, 0); + +// setTimeout(() => { +// console.log(7); +// }, 0); + +// console.log(8); + +// function main() { +// setTimeout(() => { +// console.log('load last'); +// }, 10); + +// setTimeout(() => { +// console.log('load first'); +// test(); +// }, 0); + +// test(); +// } + +// function test() { +// console.log('test'); +// } + +// main(); + +// Callback +/** + * 1. find user by username + * 2. find post by userId + * 3. find latest post + * 4. find comments by post id + * 5. find latest comment + * 6. find username of the latest commented user + */ + +/** + * /users?username=[username] + * /posts?user_id=[user_id] + * /comments?post_id=[post_id] + * /users?username=[username] + */ + +// function get(path, cb) { +// const data = {}; // somehow process it +// cb(data); +// } + +// function getUserNameFromComment(username) { +// get(`users?username=${username}`, (user) => { +// get(`posts?user_id=${user.id}`, (posts) => { +// get(`comments?post_id=${posts[0].id}`, (comments) => { +// get(`users?username=${comments[0].username}`, (user) => { +// console.log(user); +// }); +// }); +// }); +// }); +// } + +// getUserNameFromComment('arif'); + +/* const isResolved = true; + +const promise = new Promise((resolve, reject) => { + if (isResolved) { + resolve('completed'); + } else { + reject('data'); + } +}); + +console.log(promise); + +promise + .then((result) => { + console.log(result); + }) + .catch((e) => { + console.log('Rejected'); + }); */ + +// function wait(ms) { +// const promise = new Promise((resolve) => { +// setTimeout(resolve, ms); +// }); +// return promise; +// } + +// const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + +// wait(1000).then(() => { +// console.log('Done in 1000ms'); +// }); + +// wait(2000).then(() => { +// console.log('Done in 2000ms'); +// }); + +// wait(3000).then(() => { +// console.log('Done in 3000ms'); +// }); + +// const get = (url) => Promise.resolve(); + +// get(`/users?username=anarul`) +// .then((user) => { +// /** do all other operations here */ +// return get(`/posts?user_id=${user.id}`); +// }) +// .then((posts) => { +// const latestPost = posts[0]; +// return get(`/comments?post_id=${latestPost.id}`); +// }) +// .then((comments) => { +// const latestComment = comments[0]; +// return get(`/users?username=${latestComment.username}`); +// }) +// .then((user) => { +// console.log(user); +// }) +// .catch(() => { +// console.log('Error'); +// }); + +// const get = (url) => Promise.resolve(); + +// async function getUserName(username) { +// try { +// const mainUser = await get(`/users?username=${username}`); +// const posts = await get(`/posts?user_id=${mainUser.id}`); +// const comments = await get(`/comments?post_id=${posts[0].id}`); +// const user = await get(`/users?username=${comments[0].username}`); +// console.log(user); +// } catch (e) { +// console.log(e); +// } +// } + +const axios = require('axios').default; +const USERS_URL = 'https://jsonplaceholder.typicode.com/users'; +const POSTS_URL = 'https://jsonplaceholder.typicode.com/posts'; +const COMMENTS_URL = 'https://jsonplaceholder.typicode.com/comments'; + +async function getComments(username) { + try { + const { data: user } = await axios.get(`${USERS_URL}?username=${username}`); + const { data: posts } = await axios.get( + `${POSTS_URL}?userId=${user[0].id}` + ); + const { data: comments } = await axios.get( + `${COMMENTS_URL}?postId=${posts[0].id}` + ); + + const { data: userWithComment } = await axios.get( + `${USERS_URL}?email=${comments[1].email}` + ); + console.log(userWithComment); + } catch (error) { + console.log('Error Occurred', error.toJSON()); + } +} + +getComments('Bret'); + + + ``` + +
+ + +
+ package.json +

This is Source Code Of package.json

+ + + ```json +{ + "name": "lecture-10", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^0.26.1" + } +} + + + ``` + +
\ No newline at end of file diff --git a/src/lecture-10/app.js b/docs/Lectures/Fundamentals/10/app.js similarity index 100% rename from src/lecture-10/app.js rename to docs/Lectures/Fundamentals/10/app.js diff --git a/class-overview/Lecture-10/event-loop.gif b/docs/Lectures/Fundamentals/10/event-loop copy.gif similarity index 100% rename from class-overview/Lecture-10/event-loop.gif rename to docs/Lectures/Fundamentals/10/event-loop copy.gif diff --git a/resources/lecture-10/event-loop.gif b/docs/Lectures/Fundamentals/10/event-loop.gif similarity index 100% rename from resources/lecture-10/event-loop.gif rename to docs/Lectures/Fundamentals/10/event-loop.gif diff --git a/src/lecture-10/package.json b/docs/Lectures/Fundamentals/10/package.json similarity index 100% rename from src/lecture-10/package.json rename to docs/Lectures/Fundamentals/10/package.json diff --git a/docs/Lectures/Fundamentals/11/Overview.md b/docs/Lectures/Fundamentals/11/Overview.md new file mode 100644 index 0000000..b298de7 --- /dev/null +++ b/docs/Lectures/Fundamentals/11/Overview.md @@ -0,0 +1,389 @@ +## Async Iterator & Generator in JavaScript | Project Requirements + +## Introduction + +আমরা গত ক্লাসে Asynchronous Programming নিয়ে আলোচনা করেছিলাম। আজকের ক্লাসে আমরা আমাদের প্রোগ্রামিং ফান্ডামেটালস নিয়ে ক্লাস শেষ করবো। এরপর থেকে প্রজেক্ট শুরু করবো। আজকের ক্লাসের এজেন্ডাগুলো দেখা যাক একটু। + +- Iterator and Generator +- For of loop +- Async Iterator and Generator +- Project Requirements + +## Iterator + +ইটারেটর এমন একটা অবজেক্ট যা বর্তমানে কি হচ্ছে সেটা জানে, ভবিষ্যতে কিছু একটা হবে সেটা জানে, কিন্তু কি হবে তা জানে না। এটা ছাড়াও আমরা আমাদের প্রোগ্রামিং ক্যারিয়ার পার করে দিতে পারি। তাহলে আমাদের ইটারেটরের প্রয়োজন কি? এটা এমন একটা অবজেক্ট যেটা আমরা লুপ চালিয়ে কাজ করতে পারি। একটা ফর লুপ যে কাজ করে ইটারেটর দিয়েও আমরা একই কাজ করতে পারি। একটা লিস্ট যেখানে আছে সেখানেই আমাদের ইটারেট করার প্রয়োজন হয়। লুপকে আমরা বলি ইটারেট করা। ইটারেট, ইটারেবল, ইটারেটর তিনটা একই জিনিস না। ইটারেট হলো কোনো একটা লিস্টের শুরু থেকে শেষ পর্যন্ত লুপ চালিয়ে বা কোনো উপায়ে পারফর্ম করা। এর জন্য ফর লুপ আছে, জাভাস্ক্রিপ্টে forEach, map এসব আছে। এগুলো কিন্তু আবার ইটারেবলের মধ্যে পড়ে না। ইটারেবল মানে হচ্ছে যাকে ইটারেট করা সম্ভব। আর ইটারেটর হলো একটা অবজেক্ট, যা মূলত একটা ডিজাইন প্যাটার্ন। এটাকে ইটারেট করা যায়। এখন প্রশ্ন হচ্ছে যদি লুপ চালিয়ে আমরা ইটারেট করতে পারি তাহলে ইটারেটরের দরকার কি? আমরা একটা ফর লুপ দেখি। + +```js +const arr = [1, 2, 3, 4]; + +for (let i = 0; i < arr.length; i++) { + console.log(arr[i]); +} +``` + +আমরা যখন একবার লুপ চালিয়ে দিবো তখন এটা কোথাও pause করে রাখার উপায় নেই। চলা শুরু করলে পুরোটা কমপ্লিট না করে সে থামবে না। আর যদি break ইউজ করে থামিয়ে দেয়া হয় তাহলে পুরোটাই থেমে যাবে। pause করে রাখা যাবে না। ধরেন আপনি একজন টিচার। আপনি রোল কল করছেন। করতে করতে হঠাৎ একজনের সাথে গল্পে মজে গেলেন। এরপর কিছুক্ষণ পর খেয়াল হলো আপনার তো রোলকল করা বাকি। তখন যেখানে শেষ করেছিলেন সেখান থেকেই আবার শুরু করলেন। মাঝখানের যে সময়টা সেটা কিন্তু লুপ আপনাকে দিবে না। কিন্তু এই pause করার ব্যাপারটা আমাদের কিছু কিছু ক্ষেত্রে দরকার হয়। যেমন আমরা ইন্টারনেট থেকে কিছু নিয়ে আসার জন্য এই সুবিধাটা দরকার। ধরেন আপনি কিছু ডাটার জন্য রিকোয়েস্ট দিলেন। সব একসাথে আসলো না। যেটা আসলো সেটা প্রিন্ট করলেন। পরবর্তীতে আরেকটা ডাটা ক্রিয়েট হওয়ার পর আসলো, তা প্রিন্ট করলেন। এটা কখনও লুপ চালিয়ে সম্ভব না। লুপ তখনই দরকার যখন আমি কোনো ইন্টেরাপশন চাই না, আমার ডাটা ফিক্সড আছে। এই সুবিধা আমরা ইটারেটর থেকে পাবো। ধরেন আমি ইটারেটর কি চিনিই না। আমরা চাইছি অ্যারের সমস্ত ডাটা আমরা প্রিন্ট করবো। যখন আমার প্রথম ডাটা লাগবে আমি প্রথম ডাটা নিবো, যখন দ্বিতীয়টা লাগবে সেটা নিবে। এভাবে যেতে যেতে যখন আর ডাটা পাবে না তখন undefined বা false রিটার্ন করবে। ইটারেটর ব্যবহার না করে সেটা আমরা কিভাবে করতে পারি একটু দেখি। + +```js +const arr = [1, 2, 3, 4]; +let index = 0; +function next() { + return arr[index++]; +} + +console.log(next()); // 1 +console.log(next()); // 2 +console.log(next()); // 3 +console.log(next()); // 4 +console.log(next()); // undefined +``` + +যতক্ষণ ডাটা পেয়েছে ততক্ষণ সে দিয়েছে। যেই ডাটা পায়নি, সে undefined রিটার্ন করে দিয়েছে। এখন সব যে আজকে নিয়েই কাজ করতে হবে এমন কথা নেই। আমি চাই প্রথম বছর প্রথম ডাটা নিয়ে কাজ করবে, দ্বিতীয় বছর দ্বিতীয় ডাটা নিয়ে কাজ করবে। এভাবেও আমি সেট করে দিতে পারি। এখন আর কোনো লুপ নেই। আমি কন্ট্রোল করতে পারছি। আমি যদি প্রথমবার next কল করে এর মাঝখানে আরো হাজারটা কাজ করে পরের আবার কল করি দেখা যাবে সেকেন্ড কলে আউটপুট আসছে 2. মাঝখানে কি ঘটে গেলো তা বিবেচ্য বিষয় না। যতবার next কল হবে ততবার পরের ডাটা দিবে। এখানে আবার একটা সমস্যা আছে। আমাদের ট্র্যাক রাখতে হচ্ছে বিষয়টা। index আমরা গ্লোবালি নিয়েছি। যে কেউ চাইলে তা এক্সেস নিয়ে যা খুশি করে দিতে পারে। এখানেই আমাদের ইটারেটর দরকার। আমরা একটা স্ট্রিং নিই। + +```js +const channel = 'Stack'; +``` + +স্ট্রিং মূলত একটা ডাটা টাইপ হলেও এটা আসলে একটা ক্যারেক্টারের অ্যারে। এবং স্ট্রিং একটা ইটারেবল অবজেক্ট। জাভাস্ক্রিপ্টে যেকোনো কিছুকে আমরা ইটারেবল বানাতে পারি বা ইটারেবল বলতে পারি যদি তার মধ্যে একটা স্পেশাল প্রোপার্টি থাকে। সেটা কি আর কিভাবে ইটারেবল বানাবো। সেটা হলো `channel[Symbol.iterator]`. এই Symbol.iterator আগে থেকেই স্ট্রিং এর মধ্যে দেয়া আছে ফাংশন হিসেবে। আমরা যদি একটু দেখতে চাই লগ করে তাহলে দেখবো, এটা একটা ফাংশন রিটার্ন করছে। + +```js +const channel = 'Stack'; +console.log(channel[Symbol.iterator]); // [Function: [Symbol.iterator]] +console.log(channel[Symbol.iterator].toString()); // function [Symbol.iterator]() { [native code] } +``` + +যেহেতু প্রমাণ পেলাম এটা একটা ফাংশন এখন ফাংশনটাকে একটু কল করে দেখে নিই কি আউটপুট দিচ্ছে। + +```js +const channel = 'Stack'; +const channelIterator = channel[Symbol.iterator](); +console.log(channelIterator); // Object [String Iterator] {} +``` + +এটা একটা অবজেক্ট রিটার্ন করছে। এই অবজেক্টের মধ্যে তিনটা মেথড আছে। next, return, throw. আমাদের দরকার next। তাহলে এই next মেথড কল করে দেখি কি আসে। + +```js +const channel = 'Stack'; +const channelIterator = channel[Symbol.iterator](); +console.log(channelIterator.next()); // { value: 'S', done: false } +``` + +প্রথমবার next কল করলে একটা অবজেক্ট এসেছে। এখানে আমাদের value এসেছে 'Stack' এর 'S' এবং আরেকটা প্রোপার্টি এসেছে সেটা হলো `done: false`. এর মানে হলো আমার ইটারেশন এখনও শেষ হয়নি। এর মধ্যে এখনও ডাটা আছে। এবার আরো কয়েকবার next কল করে দেখা যাক। + +```js +const channel = 'Stack'; +const channelIterator = channel[Symbol.iterator](); +console.log(channelIterator.next()); // { value: 'S', done: false } +console.log(channelIterator.next()); // { value: 't', done: false } +console.log(channelIterator.next()); // { value: 'a', done: false } +console.log(channelIterator.next()); // { value: 'c', done: false } +console.log(channelIterator.next()); // { value: 'k', done: false } +console.log(channelIterator.next()); // { value: undefined, done: true } +console.log(channelIterator.next()); // { value: undefined, done: true } +``` + +দেখা যাচ্ছে একে একে পরবর্তী সব ডাটা পেয়ে গেছি। যখন আর ডাটা পায়নি তখন ভ্যালু undefined রিটার্ন করেছে এবং done true হয়ে গেছে। এর মানে ইটারেশনের কাজ শেষ। এর মধ্যে আর কোনো ডাটা নাই। + +এখন এটা করে আমাদের লাভ কি হচ্ছে? আমাদের লাভ হচ্ছে আমরা এটার কারণে for of লুপ ব্যবহার করতে পারছি। for of লুপ তখনই ব্যবহার করা যাবে যখন সেটা ইটারেটর হবে। নাহয় ব্যবহার করা যাবে না। + +```js +for (const v of channel) { + console.log(v); +} +/* +S +t +a +c +k +*/ +``` + +এখন যদি আমাদের for of লুপ না থাকতো তাহলে কিভাবে লুপ চালাতাম। সেক্ষেত্রে আমরা একটা while লুপ চালাতাম। + +```js +const channel = 'Stack'; +const channelIterator = channel[Symbol.iterator](); + +while (true) { + const data = channelIterator.next(); + if (data.done) { + break; + } + console.log(data.value); +} +``` + +এটাও ঠিক একই আউটপুট দিবে। কিন্তু এত কাজ করতে পারছি জাস্ট তিন লাইনে for of লুপ ব্যবহার করে। + +এবার আমরা আমাদের ইটারেটর বানিয়ে ফেলি। + +```js +const range = { + start: 0, + stop: 100, + step: 5, +}; +``` + +প্রথমে আমরা একটা অবজেক্ট নিলাম যেটা ০ থেকে শুরু হবে, ১০০ তে গিয়ে শেষ হবে এবং ৫ করে বৃদ্ধি পাবে। এখন এটার উপর কি for of লুপ চালানো যাবে? দেখা যাক। + +```js +for (let v of range) { + console.log(v); +} +``` + +এটা আমাদের একটা এরর দিবে এরকম `TypeError: range is not iterable` বলে। এখন প্রশ্ন হলো range কে iterable বানাবো কেমনে? চলুন একটু প্রসেসটা দেখি ইটারেবল বানানোর। + +```js +range[Symbol.iterator] = function () { + return { + next() {}, + }; +}; +``` + +প্রথমে আমাদের `range[Sybol.iterator]` নিতে হবে। এটার মধ্যে থাকবে একটা ফাংশন, যা রিটার্ন করবে একটা অবজেক্ট। আর সেই অবজেক্টের মধ্যে থাকবে next ফাংশন। এই next ফাংশন আমাদের রিটার্ন করবে দুইটা প্রোপার্টি। value এবং done. + +```js +range[Symbol.iterator] = function () { + return { + next() { + value: 0, + done: false, + }, + }; +}; +``` + +এবার যদি ফর অফ লুপ চালাই তাহলে একটা ইনফিনিটি লুপ চলতে থাকবে এবং সারা জীবন ০ দিবে। এখন অন্তত এটা বুঝা যাচ্ছে range অবজেক্ট ইটারেবল হয়েছে। এখন আমাদের next ফাংশন নিয়ে কাজ করতে হবে। + +```js +range[Symbol.iterator] = function () { + let current = this.start; + const stop = this.stop; + const step = this.step; + return { + next() { + const o = { + value: current, + done: current > stop, + }; + current += step; + return o; + }, + }; +}; +``` + +এবার আমাদের ইটারেটর ফাংশন রেডি। এবার এটাকে আমরা কল করবো। + +```js +const rangeIterator = range[Symbol.iterator](); +console.log(rangeIterator.next()); // { value: 0, done: false } +console.log(rangeIterator.next()); // { value: 5, done: false } +console.log(rangeIterator.next()); // { value: 10, done: false } +``` + +তার মানে আমাদের ইটারেটর ফাংশন কাজ করছে। এবার যদি আমরা ফর অফ লুপ চালাই তাহলে কি ঘটবে একটু দেখা যাক। + +```js +for (let v of range) { + console.log(v); +} +``` + +দেখা যাচ্ছে প্রতি ৫ ঘর পরপর ১০০ পর্যন্ত ভ্যালুগুলো আউটপুট দিয়েছে। + +## Generator + +Promise এর কাজ সহজে করার জন্য যেমন Async await এসেছে, ইটারেটরের কাজও সহজে করার জন্য এসেছে জেনারেটর। জেনারেটর ফাংশন লেখার জন্য জাস্ট funtion কীওয়ার্ডের পরে একটা (\*) চিহ্ন বসিয়ে দিলেই হয়ে যাবে। + +```js +function* myGenerator() {} +``` + +আমরা যেমন জানি ফাংশন কিছু রিটার্ন করুক বা না করুক অন্ততপক্ষে undefined রিটার্ন করবে। সেরকম জেনারেটর ফাংশন কিছু রিটার্ন করুক বা না করুক অন্ততপক্ষে ইটারেটর রিটার্ন করবে। প্রমাণ নিচে দেয়া হলোঃ + +![Genrator](./Screenshot_1.png) + +আমরা পূর্বে যেভাবে ইটারেটর বানিয়েছিলাম সেখানে প্রথম [Symbol.iterator] লিখেছিলাম, এরপর ফাংশন নিয়েছিলাম, সেটা একটা অবজেক্ট রিটার্ন করেছিল, এর ভিতরে আবার নেক্সট ফাংশন বানাতে হয়েছিল। এত কাজ আমরা খুব সহজেই জেনারেটর ফাংশন বানিয়ে করতে পারি। + +```js +function* myGenerator() { + yield 1; + yield 2; + yield 3; +} + +const iterator = myGenerator(); +console.log(iterator.next()); // { value: 1, done: false } +console.log(iterator.next()); // { value: 2, done: false } +console.log(iterator.next()); // { value: 3, done: false } +console.log(iterator.next()); // { value: undefined, done: false } +``` + +ইটারেটরের এত এত কাজ জাস্ট আমরা চার লাইনেই করে ফেললাম। yield বলতে বুঝাচ্ছে একটা ডাটা দাও একটা সময়ে। yield 1 মানে আমাকে 1 দাও, yield 2 মানে আমাকে 2 দাও। যতবার next কল করবো, তখন একটা একটা yield আমাদের আউটপুট দিবে। যখন yield শেষ হয়ে যাবে তখন undefined রিটার্ন করবে। + +এখন যদি প্রতিটা ডাটার জন্য আমাকে এসে এসে yield লিখতে হয় তাহলে তো মুশকিল। আমরা আগের প্রব্লেমটা সলভ করি। তবে এবার আর ইটারেটর বানাবো। জেনারেটর ব্যবহার করে খুব সহজেই আমরা range ফাংশন বানাবো। + +```js +function* range(start = 0, stop = 100, step = 5) { + for (let i = start; i <= stop; i += step) { + yield i; + } +} + +const rangeIt = range(1, 10, 3); +console.log(rangeIt.next()); // { value: 1, done: false } +console.log(rangeIt.next()); // { value: 4, done: false } +console.log(rangeIt.next()); // { value: 7, done: false } +console.log(rangeIt.next()); // { value: 10, done: false } +console.log(rangeIt.next()); // { value: undefined, done: true } +console.log(rangeIt.next()); // { value: undefined, done: true } +console.log(rangeIt.next()); // { value: undefined, done: true } +console.log(rangeIt.next()); // { value: undefined, done: true } +console.log(rangeIt.next()); // { value: undefined, done: true } +``` + +এটাই জেনারেটরের পাওয়ার। আমাকে আর ইটারেটর বানানোর জন্য অতকিছু লিখতে হচ্ছে না। আমি জেনারেটর ফাংশন বানিয়ে সহজেই ইটারেটর বানাতে পারছি। আমরা চাইলে ফর অফ লুপও চালাতে পারি। + +```js +for (let v of range()) { + console.log(v); +} +``` + +০ থেকে ১০০ পর্যন্ত ৫ ঘর ব্যবধানে প্রিন্ট হয়ে যাবে। জেনারেটর কত সহজ করে দিয়েছে ইটারেটরের কাজকে এটা আশা করি বুঝতে পেরেছেন। + +এখন এর রিয়েল লাইফ এক্সাম্পল কি? যেখানে আমাদের অ্যাসিনক্রোনাস টাস্কের কাজ আছে, সেখানে আমরা জেনারেটর ইটারেটরের কাজ করতে পারি। আমরা একটা আইডি মেকার বানাতে পারি। + +```js +function* generateId() { + let index = 1; + while (true) { + yield index++; + } +} + +const generateUserId = generateId(); +const generateProductId = generateId(); +console.log('User', generateUserId.next().value); // User 1 +console.log('User', generateUserId.next().value); // User 2 +console.log('User', generateUserId.next().value); // User 3 + +console.log('Product', generateProductId.next().value); // Product 1 +console.log('Product', generateProductId.next().value); // Product 2 +console.log('Product', generateProductId.next().value); // Product 3 +console.log('Product', generateProductId.next().value); // Product 4 +console.log('Product', generateProductId.next().value); // Product 5 +console.log('Product', generateProductId.next().value); // Product 6 +``` + +## Async iterator and Async generator + +Async iterator and Async generator মূলত অ্যাসিনক্রোনাস টাস্কের জন্য ব্যবহৃত হয়। চলুন আমরা একটা উদাহরণ দেখি। আমরা চাইছি আমাদের API থেকে আমরা আমাদের ইউজারকে বের করে আনতে। + +```js +const axios = require('axios').default; + +async function getUsers() { + const url = 'https://jsonplaceholder.typicode.com/users'; + const { data: users } = await axios.get(url); + return users; +} + +getUsers() + .then(async (users) => { + console.log(users); + }) + .catch((e) => { + console.log(e); + }); +``` + +আমরা ইউজার পাচ্ছি। কিন্তু এই ডাটা দিয়ে আমাদের কাজ নেই। আমাদের দরকার এই ইউজারের কতগুলো পোস্ট আছে তা নির্দিষ্ট সময় পরপর নিয়ে আসা। সেক্ষেত্রে আমরা একটা জেনারেটর ফাংশন বানাতে পারি। + +```js +async function* getPostsByUser(users) { + const url = 'https://jsonplaceholder.typicode.com/posts'; + for (let i = 0; i < users.length; i++) { + const { data: posts } = await axios.get(`${url}?userId=${users[i].id}`); + yield posts; + } +} +``` + +এখানে আমরা আগে যে ইউজার ডাটা পেয়েছিলাম সেটাকে প্যারামিটার হিসেবে নিয়ে আসলাম। এরপর ফর লুপ চালিয়ে প্রতিটা পোস্ট আমরা yield করে রাখবো। যখন প্রয়োজন হবে তখন আমরা একটা একটা করে নিয়ে আসবো। যেহেতু এটা async টাস্ক তাই এখানে async await ইউজ করা হয়েছে। এটাকে বলে async generator. + +এবার আমরা এই ফাংশন থেকে একটা ইটারেটর বানিয়ে ফেলতে পারি। + +```js +getUsers() + .then(async (users) => { + const userIterator = await getPostsByUser(users); + await userIterator.next(); + await userIterator.next(); + console.log((await userIterator.next()).value); + }) + .catch((e) => { + console.log(e); + }); +``` + +যেহেতু এখানে প্রথমে দুইবার ইটারেটরের নেক্সট ফাংশন কল হয়ে গেছে তাই প্রিন্ট হবে ৩ নাম্বার ইউজার আইডির পোস্টগুলো। এবার আশা করি আপনারা বুঝতে পারছে ইটারেটরের কাজটা ঠিক কোথায়। আমি লুপ চালালে কিন্তু শুধু ৩ নাম্বার আইডির জন্য ডাটা আনতে পারতাম না। জেনারেটর আরো সহজ করে দিয়েছে ইটারেটরের কাজ। + +এবার আমি যদি চাই একসাথে সব ইউজারের ডাটা দেখতে তাহলে ফর অফ লুপ চালিয়ে দিলেই কাজ শেষ। + +```js +getUsers() + .then(async (users) => { + for await (let v of getPostsByUser(users)) { + console.log(v); + } + }) + .catch((e) => { + console.log(e); + }); +``` + +সব ইউজারের ডাটা আমরা পেয়ে যাবো। + +আমরা চাইলে উপরের কাজকে একটু অন্যভাবে করতে পারি। + +```js +const axios = require('axios').default; + +async function getUsers() { + const url = 'https://jsonplaceholder.typicode.com/users'; + const { data: users } = await axios.get(url); + return users.map((user) => + axios.get(`https://jsonplaceholder.typicode.com/posts?userId=${user.id}`) + ); +} + +(async () => { + const users = await getUsers(); + for await (let v of users) { + console.log(v.data); + } +})(); +``` + +আমরা এখানে getUsers ফাংশন থেকে কোনো ইউজার রিটার্ন করবো না। আমরা রিটার্ন করবো প্রতিটা ইউজারের জন্য একটা প্রমিজ বা API কল করে রাখা যেন আমরা যখন চাই তখন পোস্ট বের করে আনতে পারি। তাহলে আর আমাদের জেনারেটর ফাংশন বা ইটারেটরের কোনো প্রয়োজন হচ্ছে না। তবে এখানে আমরা কোনো কন্ট্রোল পাবো না। সব একসাথে চলে আসবে ডাটা। + +## Project Requirements + +আমাদের প্রজেক্ট শুরু হতে যাচ্ছে। প্রথম প্রজেক্ট হিসেবে আমরা একটা অ্যাটেন্ডেন্স সিস্টেম বানাবো। সেই সিস্টেমের জন্য আমাদের ক্লায়েন্ট কিছু রিকোয়ারমেন্ট দিয়েছেন। সেটা নিচে দেয়া হলো। + +We need an attendance system. Students can create their own profile. Admin can see list of students and their attendances. Admin can enable and disable attend button. Also this button can be disabled based on a timer. Each time admin enable attend button, students can participate for only once. Each day, student will have a time sheet of attendance. + +Student can see their own time logs and attend button when enable. + +এর পরের ক্লাস থেকে আমরা একজন ডেভেলপার কিভাবে কোনো প্রজেক্ট শুরু করবো সেগুলো ধাপে ধাপে দেখবো। + + + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/docs/Lectures/Fundamentals/11/Resource.md b/docs/Lectures/Fundamentals/11/Resource.md new file mode 100644 index 0000000..96c047c --- /dev/null +++ b/docs/Lectures/Fundamentals/11/Resource.md @@ -0,0 +1,229 @@ +## Async Iterator & Generator in JavaScript | Project Requirements + +## Agenda + +- Iterator and Generator +- For of loop +- Async Iterator and Generator +- Github Collaboration +- Project Requirements + +--- + +### Iterator + +We use iterator because we cannot pause a loop. When a loop starts it will not stop until the execution of last element. Let's look the below example: + +```js +const arr = [1, 2, 3, 4]; +let index = 0; +function next() { + return arr[index++]; +} + +console.log(next()); // 1 +console.log(next()); // 2 +console.log(next()); // 3 +console.log(next()); // 4 +console.log(next()); // undefined +``` + +In iterator if there is no value it returns simply `undefined`. + +```js +const channel = 'Stack'; +const channelIterator = channel[Symbol.iterator](); +console.log(channelIterator.next()); // { value: 'S', done: false } +console.log(channelIterator.next()); // { value: 't', done: false } +console.log(channelIterator.next()); // { value: 'a', done: false } +console.log(channelIterator.next()); // { value: 'c', done: false } +console.log(channelIterator.next()); // { value: 'k', done: false } +console.log(channelIterator.next()); // { value: undefined, done: true } +console.log(channelIterator.next()); // { value: undefined, done: true } +``` + +```js +const range = { + start: 0, + stop: 100, + step: 5, +}; +range[Symbol.iterator] = function () { + let current = this.start; + const stop = this.stop; + const step = this.step; + return { + next() { + const o = { + value: current, + done: current > stop, + }; + current += step; + return o; + }, + }; +}; + +for (let v of range) { + console.log(v); +} +``` + +--- + +#### Generator + +Generator is used to create an iterator more easily. Generator always returns an iterator. To create a `generator` function we need to add an asterisk(\*) after `function` keyword like this `function*`. For example: + +```js +function* myGenerator() { + yield 1; + yield 2; + yield 3; +} + +const iterator = myGenerator(); +console.log(iterator.next()); +console.log(iterator.next()); +console.log(iterator.next()); +console.log(iterator.next()); +``` + +```js +function* range(start = 0, stop = 100, step = 5) { + for (let i = start; i <= stop; i += step) { + yield i; + } +} + +// const rangeIt = range(1, 10, 3); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); + +for (let v of range()) { + console.log(v); +} +``` + +```js +function* generateId() { + let index = 1; + while (true) { + yield index++; + } +} + +const generateUserId = generateId(); +const generateProductId = generateId(); +console.log('User', generateUserId.next().value); +console.log('User', generateUserId.next().value); +console.log('User', generateUserId.next().value); + +console.log('Product', generateProductId.next().value); +console.log('Product', generateProductId.next().value); +console.log('Product', generateProductId.next().value); +console.log('Product', generateProductId.next().value); +console.log('Product', generateProductId.next().value); +console.log('Product', generateProductId.next().value); +``` + +--- + +##### for of loop + +```js +const channel = 'Stack'; +const channelIterator = channel[Symbol.iterator](); +for (let v of channel) { + console.log(v); // S t a c k +} +``` + +--- + +##### Async Iterator and Generator + +```js +const axios = require('axios').default; + +async function getUsers() { + const url = 'https://jsonplaceholder.typicode.com/users'; + const { data: users } = await axios.get(url); + return users; +} + +async function* getPostsByUser(users) { + const url = 'https://jsonplaceholder.typicode.com/posts'; + for (let i = 0; i < users.length; i++) { + const { data: posts } = await axios.get(`${url}?userId=${users[i].id}`); + yield posts; + } +} + +getUsers() + .then(async (users) => { + // const userIterator = await getPostsByUser(users); + // await userIterator.next(); + // await userIterator.next(); + // console.log((await userIterator.next()).value); + + for await (let v of getPostsByUser(users)) { + console.log(v.map((d) => d.title)); + } + }) + .catch((e) => { + console.log(e); + }); +``` + +We can write the above program as below: + +```js +const axios = require('axios').default; + +async function getUsers() { + const url = 'https://jsonplaceholder.typicode.com/users'; + const { data: users } = await axios.get(url); + return users.map((user) => + axios.get(`https://jsonplaceholder.typicode.com/posts?userId=${user.id}`) + ); +} + +(async () => { + const users = await getUsers(); + for await (let v of users) { + console.log(v.data.map((post) => post.title)); + } +})(); +``` + +--- + +##### Project Requirements + +We need an attendance system. Students can create their own profile. Admin can see list of students and their attendances. Admin can enable and disable attend button. Also this button can be disabled based on a timer. Each time admin enable attend button, students can participate for only once. Each day, student will have a time sheet of attendance. + +Student can see their own time logs and attend button when enable. + +--- + +##### References + +- [Iterators and generators - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators) +- [JavaScript iterators and generators: A complete guide - LogRocket Blog](https://blog.logrocket.com/javascript-iterators-and-generators-a-complete-guide/) +- [for await...of - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of) +- [JavaScript async iterators](https://www.nodejsdesignpatterns.com/blog/javascript-async-iterators/) +- [Class Overview](../../Class%20Overview/Lecture-11/README.md) + +--- + +##### Source Code + +- [Source Code](../../src/lecture-11/app.js) diff --git a/class-overview/Lecture-11/Screenshot_1.png b/docs/Lectures/Fundamentals/11/Screenshot_1.png similarity index 100% rename from class-overview/Lecture-11/Screenshot_1.png rename to docs/Lectures/Fundamentals/11/Screenshot_1.png diff --git a/docs/Lectures/Fundamentals/11/SourceCode.md b/docs/Lectures/Fundamentals/11/SourceCode.md new file mode 100644 index 0000000..0e34fb2 --- /dev/null +++ b/docs/Lectures/Fundamentals/11/SourceCode.md @@ -0,0 +1,199 @@ +
+ app.js +

This is Source Code Of app.js

+ + + ```javascript +// const arr = [1, 2, 3, 4]; +// let index = 0; +// function next() { +// return arr[index++]; +// } + +// console.log(next()); +// console.log(next()); + +// const channel = 'Stack'; +// const channelIterator = channel[Symbol.iterator](); +// console.log(channelIterator.next()); +// console.log(channelIterator.next()); +// console.log(channelIterator.next()); +// console.log(channelIterator.next()); +// console.log(channelIterator.next()); +// console.log(channelIterator.next()); +// console.log(channelIterator.next()); + +// for (let v of channel) { +// console.log(v); +// } + +// while (true) { +// const data = channelIterator.next(); +// if (data.done) { +// break; +// } +// console.log(data.value); +// } + +// const range = { +// start: 0, +// stop: 100, +// step: 5, +// }; +// range[Symbol.iterator] = function () { +// let current = this.start; +// const stop = this.stop; +// const step = this.step; +// return { +// next() { +// const o = { +// value: current, +// done: current > stop, +// }; +// current += step; +// return o; +// }, +// }; +// }; + +// for (let v of range) { +// console.log(v); +// } + +// const rangeIterator = range[Symbol.iterator](); +// console.log(rangeIterator.next()); +// console.log(rangeIterator.next()); +// console.log(rangeIterator.next()); + +// function* myGenerator() { +// yield 1; +// yield 2; +// yield 3; +// } + +// const iterator = myGenerator(); +// console.log(iterator.next()); +// console.log(iterator.next()); +// console.log(iterator.next()); +// console.log(iterator.next()); + +// Generator always returns iterator + +// function* range(start = 0, stop = 100, step = 5) { +// for (let i = start; i <= stop; i += step) { +// yield i; +// } +// } + +// const rangeIt = range(1, 10, 3); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); + +// for (let v of range()) { +// console.log(v); +// } + +// function* generateId() { +// let index = 1; +// while (true) { +// yield index++; +// } +// } + +// const generateUserId = generateId(); +// const generateProductId = generateId(); +// console.log('User', generateUserId.next().value); +// console.log('User', generateUserId.next().value); +// console.log('User', generateUserId.next().value); + +// console.log('Product', generateProductId.next().value); +// console.log('Product', generateProductId.next().value); +// console.log('Product', generateProductId.next().value); +// console.log('Product', generateProductId.next().value); +// console.log('Product', generateProductId.next().value); +// console.log('Product', generateProductId.next().value); +const axios = require('axios').default; + +async function getUsers() { + const url = 'https://jsonplaceholder.typicode.com/users'; + const { data: users } = await axios.get(url); + return users.map((user) => + axios.get(`https://jsonplaceholder.typicode.com/posts?userId=${user.id}`) + ); +} + +// async function* getPostsByUser(users) { +// const url = 'https://jsonplaceholder.typicode.com/posts'; +// for (let i = 0; i < users.length; i++) { +// const { data: posts } = await axios.get(`${url}?userId=${users[i].id}`); +// yield posts; +// } +// } + +// getUsers() +// .then(async (users) => { +// // const userIterator = await getPostsByUser(users); +// // await userIterator.next(); +// // await userIterator.next(); +// // console.log((await userIterator.next()).value); + +// // for await (let v of getPostsByUser(users)) { +// // console.log(v.map((d) => d.title)); +// // } + +// console.log(users); +// }) +// .catch((e) => { +// console.log(e); +// }); + +(async () => { + const users = await getUsers(); + for await (let v of users) { + console.log(v.data.map((post) => post.title)); + } +})(); + + + + ``` + +
+ + + +
+ app.js +

This is Source Code Of app.js

+ + + ```json +{ + "name": "lecture-11", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^0.27.2" + } +} + + ``` + +
+ + + diff --git a/src/lecture-11/app.js b/docs/Lectures/Fundamentals/11/app.js similarity index 100% rename from src/lecture-11/app.js rename to docs/Lectures/Fundamentals/11/app.js diff --git a/src/lecture-11/package.json b/docs/Lectures/Fundamentals/11/package.json similarity index 100% rename from src/lecture-11/package.json rename to docs/Lectures/Fundamentals/11/package.json diff --git a/docs/Lectures/Fundamentals/12/Overview.md b/docs/Lectures/Fundamentals/12/Overview.md new file mode 100644 index 0000000..9c9084b --- /dev/null +++ b/docs/Lectures/Fundamentals/12/Overview.md @@ -0,0 +1,195 @@ +## Attendance System Requirement Analysis + +## Introduction + +গত ক্লাসে আমরা আমাদের প্রজেক্টের বেসিক যে রিকোয়ারমেন্ট ক্লায়েন্ট থেকে পেয়েছিলাম সেটা দেখেছিলাম। সেটা আলোকে আজ আমরা এর ফাংশনাল রিকোয়ারমেন্টগুলো অ্যানালাইসিস করবো। একটা বেসিক SRS (Software Requirement Specification) বানাবো। আর এই প্রজেক্ট করতে যে যে টেকনোলজি লাগবে তা চুজ করবো। আজকের এজেন্ডাগুলো তাহলে লিখে ফেলা যাক। + +- Functional Requirement Analysis +- Create A Basic SRS +- Choose Technologies + +প্রোগ্রামার হতে হলে সবচেয়ে বেশি যে বিষয়ে জোর দেয়া লাগে সেটা হলো প্রব্লেম ক্রিয়েশন বা প্রব্লেম ফাইন্ডিং। আমরা সবচেয়ে বেশি জোর দিই প্রব্লেম সলভিং এর দিকে। কিন্তু সবচেয়ে কঠিন কাজ দুই লাইনের একটা রিকোয়ারমেন্ট থেকে ছোট ছোট প্রব্লেম বের করে আনা। যেমন আমি চাইছি এই পেইজে ঢুকলে একটা বল স্ক্রিনের নিচের edge থেকে উপরের edge এ বাউন্স করবে। জাস্ট এক লাইনের রিকোয়ারমেন্ট। কিন্তু এটা করার জন্য আমরা ডেভেলপাররা আমাদের মতো করে প্রব্লেমটাকে সাজিয়ে নিবো। কিভাবে? আমাদের নিচের edge এ যখন বল থাকবে তখন সেটা উপরের দিকে উঠবে একটা নির্দিষ্ট কোণে। ধরে নিলাম বলটি প্রথম অবস্থায় (০, ০) অবস্থানে আছে। এখন যখন সে উপরের দিকে যাবে তখন সে এক্স অক্ষের সাথে ৭০ ডিগ্রি কোণে উঠবে। এরপর উপরের edge এ যখন সে টাচ করবে আবার ৭০ ডিগ্রি কোণে নিচের দিকে নামবে। এভাবে প্রব্লেমটাকে আমরা আমাদের মতো করে ভাগ করে নিয়ে প্রতিটা প্রব্লেম ধরে ধরে কাজ করবো। যে কোনো প্রজেক্টের ক্ষেত্রেই আমাদের প্রথম ক্লায়েন্টের রিকোয়ারমেন্ট থেকে আমরা আমাদের মতো প্রব্লেম বের করে আনবো। এবার সেই প্রব্লেম সলভ করবো। একে বলা হয় ফাংশনাল রিকোয়ারমেন্ট অ্যানালাইসিস। রিকোয়ারমেন্ট অ্যানালাইসিস দুই ধরণের - ফাংশনাল এবং নন ফাংশনাল। এটা নিয়ে [লেকচার ১](../Fundamentals/Welcome%20Decision%20Making/Lecture%2001/README.md) এ ভালভাবে আলোচনা করা হয়েছে। আমরা এবার আমাদের প্রজেক্টের জন্য ফাংশনাল রিকোয়ারমেন্ট অ্যানালাইসিস করবো। প্রথমে আমরা আমাদের ক্লায়েন্টের রিকোয়ারমেন্ট লিখে ফেলি। + +## Client's Requirements + +We need an attendance system. Students can create their own profile. Admin can see list of students and their attendances. Admin can enable and disable attend button. Also this button can be disabled based on a timer. Each time admin enable attend button, students can participate for only once. Each day, student will have a time sheet of attendance. + +Student can see their own time logs and attend button when enable. + +## Functional Requirements + +রিকোয়ারমেন্ট এবং রিকোয়ারমেন্ট অ্যানালাইসিসের মধ্যে পার্থক্য আছে। আমরা এই দুইটাকে একসাথে ধরে গুলিয়ে ফেলি। রিকোয়ারমেন্ট হলো আমার এই অ্যাপ্লিকেশন কি কি ফিচার্স থাকবে। আর রিকোয়ামেন্ট অ্যানালাইসিস হলো ঐ ফিচারগুলোকে কিভাবে ইমলিমেন্ট করবো। এখন আমরা আগে আমাদের ফাংশনাল রিকোয়ামেন্টস লিখে ফেলি। এরপর অ্যানালাইসিস করবো। এই অ্যাপের দুইটা End User আছে। একটা হলো Admin অন্যটা Student. মূলত এই দুইজনই আমাদের অ্যাপ ইউজ করবে। প্রথমে আমরা Student এর জন্য কি কি রিকোয়ারমেন্ট আছে তা লিখে ফেলি। + +**Student Roles:** + +- Student can register themselves. +- There will be following account status for a student: + - Pending + - Active + - Reject +- Student can login with their credentials. +- Pending and rejected students won't have anything in their profile. +- Active students can update their profile info. + - First Name + - Last Name + - Email + - Phone No + - Profile Picture +- Active student can change / update password. +- Active students can see their time sheet + - Calender view + - List view + - Table view +- Active students can participate in attendance system. +- Student can logout. + +এবার Admin এর কি কি কাজ থাকতে পারে তা লিখে ফেলি। Admin এর কথা বলতে গেলে দুইটা কনসেপ্ট চলে আসে। সুপার অ্যাডমিন আর অ্যাডমিন। সুপার অ্যাডমিনের কাজ হলো কাকে কাকে অ্যাডমিন বানাবে তা ঠিক করা। সুপার অ্যাডমিন না থাকলে তো যে কেউই গিয়ে অ্যাডমিন হিসেবে সাইন আপ করে ফেলতে পারে। সুপার অ্যাডমিন অনেকভাবে ক্রিয়েট করার যায়। নিচে কিছু পদ্ধতি দেয়া হলো। + +- আমরা অ্যাপ্লিকেশন ডিজাইনের সময়ই আমাদের এনভায়রনমেন্ট ভ্যারিয়েবলের মধ্যে সুপার অ্যাডমিনের ইনফরমেশন দিয়ে দিতে পারি। যখন অ্যাপ্লিকেশন রান হবে তখন অটোমেটিক্যালি সেখানে থেকে ইনফরমেশন নিয়ে সুপার অ্যাডমিন ক্রিয়েট করে ফেলতে পারে। +- অ্যাপ্লিকেশনে যে প্রথম অ্যাডমিন হিসেবে অ্যাপ্লিকেশন ডেপ্লয় করবে তাকে সুপার অ্যাডমিন হিসেবে ধরে নেয়া যেতে পারে। মোটামুটি সব ক্ষেত্রেই এখন এই পদ্ধতি ব্যবহার করা হচ্ছে। +- আরেকটা হচ্ছে আমরা একটা cli টুলস বানিয়ে কমান্ডের মাধ্যমেও সুপার অ্যাডমিন ক্রিয়েট করতে পারি। + +আমাদের এই অ্যাপ্লিকেশনে ধরে নিলাম একজনই অ্যাডমিন। মাল্টি অ্যাডমিন রোলে আমরা যাচ্ছি না প্রজেক্টটাকে সিম্পল রাখার জন্য। ধরে নিলাম একজনই অ্যাডমিন এবং সেই সুপার অ্যাডমিন। আমাদের খেয়াল রাখতে হবে আমাদের রিকোয়ারমেন্টস যেন ক্লায়েন্টের রিকোয়ারমেন্টের বাইরে না যায়। ক্লায়েন্টের রিকোয়ারমেন্টসে মাল্টি অ্যাডমিনের কথা বলা নাই, তাই আমরা মাল্টি অ্যাডমিন নিয়ে ভাববো না। এখন অ্যাডমিনের কাজগুলো কি কি হবে? + +**Admin Roles:** + +- Admin can create student. +- Admin can delete / update / check students information. +- Admin can change status of a student. +- Admin can check time sheet of a student. +- Admin can enable or disable attendance button. +- Admin can check stats of a given day. + +আমাদের রিকোয়ারমেন্ট নেয়া শেষ। এবার আমরা রিকোয়ারমেন্ট অ্যানালাইসিস করবো। + +## Requirements Analaysis + +আপনারা যদি একটু গুগলে সার্চ করেন দেখবেন অনেক বড় বড় বিভিন্ন ফরমেটেড অ্যানালাইসিস পাবেন। আমাদের অ্যাপ্লিকেশনটা যেহেতু সিম্পল তাই আমাদের এখন অতো জটিল, ফরমেট মেইনটেইন করে অ্যানালাইসিস করার দরকার নেই। আমরা সিম্পল একটা অ্যানালাইসি বানাবো। + +### Models + +আমাদের এখানে দুইটা ইউজার আছে - Admin & Student। আমরা প্রতিটা ইউজারের জন্য মডেল বানিয়ে ফেলতে পারি। কিন্তু Admin এর ক্ষেত্রেও আমাদের লাগবে Name, Email, Password আর Student এর জন্য লাগবে Name, Email, Password, AccountStatus। এখন দেখেন Admin আর Student এর জন্য Name, Email, Password আছে। আমরা চাইলে ডাটাবেইজে দুইটা ডিফারেন্ট টেবিল বা কালেকশন তৈরি করে কানেক্ট করতে পারি। কিন্তু দুইটা ভিন্ন ভিন্ন কালেকশন বা টেবিলের মধ্যে সম্পর্ক স্থাপন কথা অনেক প্যাড়াদায়ক কাজ। আবার এমনও হতে পারে ভবিষ্যতে Student থেকে দুইজনকে অ্যাডমিন বানানো হলো কাজের সুবিধার্থে। তাহলে তার দুইটা ভিন্ন প্রোফাইল থাকবে, দুইটা ভিন্ন রোল থাকবে। সে একসাথে Student আবার Admin। কিন্তু এখানে আরেকতা প্রব্লেম আছে। আমি যখন Student কে Admin রোল দিবো তখন সে পুরো পাওয়ার পেয়ে যাবে। সে চাইলে নিজের অ্যাটেনডেন্স কোনোদিন না সলে দিয়ে দিতে পারে, বা কারো সাথে সম্পর্ক খারাপ তার অ্যাটেনডেন্স সে ডিলিট করে দিতে পারবে। আমরা তো এমন হতে দিতে পারি না। আমরা শুধু তাকে অ্যাটেনডেন্স বাটন এনেবল বা ডিজেবল করার পাওয়ার দিবো আর কিছু না। তাহলে এখানে তিনটা ডিফারেন্ট রোল থাকবে - Admin, Student, Moderator। এখন আমরা Admin & Student এর জন্য আলাদা আলাদাভাবে মডেল না নিয়ে User নামে একটা মডেল নিয়ে নিবো যাতে Roles থাকবে, যেটা দিয়ে আমরা বুঝবো সে অ্যাডমিন নাকি ছাত্র নাকি মডারেটর। এখন AccountStatus তো শুধু Student এর জন্য প্রযোজ্য। কিন্তু আমরা যেহেতু এখানে মডারেটর বা মাল্টি অ্যাডমিন নিয়ে কাজ করার বিষয় ভাবছি আমরা চাইলে কোনো অ্যাডমিনকে পেন্ডিং রাখতে পারি বা রিজেক্ট করে দিতে পারি। যদি কিছুই না থাকে বাই ডিফল্ট তা Active দেখাতে পারে। তাই AccountStatus আমরা User এর ক্ষেত্রে লিখতে পারি। + +**User:** + +- Name +- Email +- Password +- Roles +- AccountStatus + +এবার দরকার প্রোফাইলের জন্য একটা মডেল। + +**Profile:** + +- First Name +- Last Name +- Phone No +- Profile Picture +- UserId + +এখন দরকার Student Attendance এর জন্য একটা মডেল। এখানে মূলত থাকবে Student এর আইডি আর কখন তার অ্যাটেনডেন্স ক্রিয়েট হয়েছে সেটা। এখন একদিনে ৩টা অ্যাটেনডেন্সও দিতে পারে। সেটা ঐ দিনের অ্যাটেনডেন্সের একটা গ্রুপ হিসেবে থাকবে। এছাড়া কোন অ্যাটেনডেন্সের আন্ডারে আমরা অ্যাটেনডেন্স দিচ্ছি তার জন্য আমাদের একটা আইডি লাগবে। + +**StudentAttendance:** + +- UserId +- CreatedAt: DateTime +- AdminAttendanceId + +এখন আমার যে API যেটার মাধ্যমে অ্যাটেনডেন্স দিবে সেটা ট্র্যাক করবে কে? আমাদের অ্যাডমিন যে ক্লিক করে এনেবল করে দিবে যে এখন অ্যাটেনডেন্স দেয়া যাবে এই সিস্টেমটা কে মেইনটেইন করবে? কোথায় থাকবে এটা? এটাও একটা কমপ্লেক্স বিষয়। আমাদের একটা বুলিয়ান ভ্যারিয়েবল দরকার, যেখানে আমাদের ডাটা ট্র্যাক করা থাকবে, যে এখন টাইম হয়ে গেছে তুমি এনেবল করতে পারো। আবার নির্দিষ্ট সময় পর তা ডিজেবল করতে পারো। এটা জন্য আমাদের একটা মডেল দরকার। আমরা AdminAttendance নামের একটা মডেল বানাই। এখানে কোন টাইমে সে সিস্টেমটা ক্রিয়েট করবে সেটা থাকবে। এখন একদিনে অ্যাডমিন কয়েকবার সিস্টেম এনেবল করতে পারে। তাহলে এখানে আগের মতোই আমাদের DateTime ব্যবহার করতে হবে। ক্লায়েন্টের রিকোয়ারমেন্টে লেখা আছে বাটন এনেবল করার পর একটা টাইমারের উপর ডিপেন্ড করে তা কিছু সময় পর ডিজেবল হয়ে যাবে। সুতরাং এখানে একটা টাইম লিমিটের ব্যাপার আছে। এখানে ডিফল্ট টাইম থাকতে পারে ৫ মিনিট, আবার ইউজার সেট করে দিতে পারে। আবার যেহেতু এনেবল ডিজেবল ব্যাপার আছে তাই একটা স্ট্যাটাস থাকতে পারে। Active or completed. + +**AdminAttendance:** + +- CreatedAt: DateTime +- Status +- TimeLimit + +আমাদের মডেল তৈরি করা শেষ। + +আমরা দুইভাবে অ্যাপ্লিকেশন বানাতে পারি - সিংগেল পেইজ এবং মাল্টি পেইজ। আমাদের এই অ্যাপ্লিকেশন সিংগেল পেইজ অ্যাপ্লিকেশন। আর সিংগেল পেইজ অ্যাপ্লিকেশনে কমিউনিকেশন করার জন্য, ড্যাশবোর্ড নিয়ে কাজ করার জন্য দরকার API. এখন বড় সিদ্ধান্ত নিতে হবে আমরা কিভাবে API বানাবো, আমাদের কি কি End point লাগবে? সেগুলো আমাদের রিকোয়ারমেন্টস থেকে খুঁজে নিয়ে আসতে হবে। + +### Endpoints + +**Student Endpoints:** + +- POST /auth/login [public] +- POST /auth/register [public] +- PATCH /profiles [private] +- PATCH /profiles/avatar [private] +- PUT /auth/change-password [private] +- GET /timesheet [private] +- GET /attendance [private] +- GET /attendanceStatus [private] + +**Admin Endpoints:** + +- GET /users [private] +- POST /users [private] +- PATCH /users/userId [private] +- DELETE /users/userId [private] +- GET /users/userId [private] +- GET /profiles [private] +- POST /profiles [private] +- PATCH /profiles/profileId [private] +- DELETE /profiles/profileId [private] +- GET /profiles/profileId [private] +- GET /timesheet/userId [private] +- GET /timesheet/stats [private] +- POST /attendance/enable [private] +- GET /attendance/disabled/:attendanceId [private] + +এই গেলো মোটামুটি আমাদের Routes. যে রাউটস দিয়ে আমরা যে অ্যাপ্লিকেশন বানাতে চাইছি তা সুন্দরভাবে বানিয়ে ফেলতে পারি। এখানে GET, POST, PUT, PATCH, DELETE অনেকে ধরণের লেখা দেখা যাচ্ছে। এগুলো দিয়ে মূলত কোন রাউটের রিকোয়েস্ট কিভাবে হ্যান্ডেল হবে তা বুঝায়। আমরা কাজ করতে গেলে এগুলো সম্পর্কে জানতে পারবো। জাস্ট একটা বেসিক আইডিয়া এখানে দেয়া হলো। + +- GET: যখন কোনো কিছু থেকে আমাদের ডাটা retrieve করার প্রয়োজন হয় তখন GET ব্যবহার হয়। +- POST: যখন আমাদের কোনো কিছু আপডেট করার প্রয়োজন হয় বা কোনো স্টেট চেইঞ্জের প্রয়োজন হয় তখন আমরা POST ব্যবহার করি। +- PUT: যখন আমাদের বর্তমান অবস্থা রিপ্লেস করার প্রয়োজন হয় তখন আমরা PUT ব্যবহার করি। যেমন পাসওয়ার্ড চেইঞ্জ করার জন্য আমরা PUT ইউজ করেছি, কারণ বর্তমান পাসওয়ার্ড আমরা পুরোপুরি চেইঞ্জ করতে চাইছি। +- PATCH: যখন আমরা কিছু পুরোপুরি রিপ্লেস বা চেইঞ্জ করবো না, কিছুটা চেইঞ্জ করবো, সহজ ভাষায় মডিফাই করার প্রয়োজন পরবে তখন PATCH ইউজ করবো। +- DELETE: যখন কোনো ডাটা ডিলিট করার প্রয়োজন পড়বে তখন আমরা DELETE ব্যবহার করবো। + +public route বলতে বুঝায় এটা যে কেউ দেখতে পারবে। আর private route বলতে বুঝাচ্ছে শুধুমাত্র লগইন করা ইউজাররাই এগুলোর এক্সেস পাবে। মানে এগুলো যে কেউ দেখতে পারবে না। + +আমরা আমাদের যে Workflow, সেটাকে একটু ভিজ্যুয়ালি দেখার চেষ্টা করি। + +## Visualize our workflow + +প্রথমে আমরা কথা বলবো রেজিস্ট্রেশন এবং অথেনটিকেশন সিস্টেম নিয়ে। + +![Registration](./registration-process.jpg) + +আমরা প্রথমে `/auth/register` এ রিকোয়েস্ট পাঠাবো JSON আকারে। সে রিকোয়েস্ট আমাদের সার্ভার নিয়ে প্রথমে ভ্যালিডেট করবে। এরপর একটা নির্দিষ্ট ফর্মে ট্রান্সফর্ম করবে। এরপর আমাদের পাসওয়ার্ড হ্যাশ করবে যাতে ডাটাবেইজ হ্যাক হলেও আমাদের পাসওয়ার্ড হ্যাকার না পায়। এভাবে ডাটা প্রসেস করে সেগুলো ডাটাবেইজে পাঠিয়ে দিবে। সাথে সাথে একটা ম্যাসেজ রিটার্ন করবে। যদি সাক্সেসফুলি সব হয় তাহলে সে একটা `Success 201` ম্যাসেজ রিটার্ন করবে। যদি ইউজারের ডাটায় কোনো ভুল থাকে তাহলে `Error 400` ম্যাসেজ রিটার্ন করবে। আর যদি সার্ভারের কোনো সমস্যার কারণে এরর হয় তাহলে `Error 500` ম্যাসেজ রিটার্ন করবে। + +এবার আসি লগইন সিস্টেমে। + +![Login](./login-process.jpg) + +আমরা যখন আমাদের সার্ভারে `/auth/login` এ রিকোয়েস্ট পাঠাবো তখন আমাদের ইমেইল আর পাসওয়ার্ড প্রোভাইড করবো এরপর সার্ভার প্রসেসিং শুরু করবে। প্রথমে ডাটা ভ্যালিডেট করবে। এরপর ইমেইলটা আমাদের ডাটাবেইজে সার্চ দিয়ে দেখবে ঐ ইমেইল দিয়ে কোনো ইউজার আছে কিনা। যদি না থাকে এরর ৪০০ থ্রো করবে। আর যদি থাকে তাহলে পাসওয়ার্ড চেক করবে। যদি পাসওয়ার্ড না মিলে এরর ৪০০ থ্রো করবে। আর যদি মিলে তাহলে একটা JWT Token জেনারেট করবে। এরপর এই টোকেন সহ একটা সাক্সেস ২০০ ম্যাসেজ রিটার্ন করবে। সাক্সেসফুলি লগইন হওয়ার পর আমরা JWT Token লোকাল ডাটাবেইজে সেভ করে রাখবে, কারণ সমস্ত প্রাইভেট রাউটের রিকোয়েস্ট পাঠানোর জন্য আমরা এই টোকেনটা পাঠাবো। টোকেন সেইভ করার সাথে সাথে Student Dashboard এ রিডিরেক্ট হয়ে যাবে। + +ড্যাশবোর্ডে যাওয়ার পরে ইউজার কি করবে। সে তার টাইমশীট দেখতে চাইবে। দেখার জন্য সার্ভারের কাছে `/timesheet` রিকোয়েস্ট পাঠাবে। এখন আমরা আমাদের endpoint এ লিখেছি এটা একটা প্রাইভেট রাউট তাই রিকোয়েস্টের সাথে সাথে অথেনটিকেশন হেডার হিসেবে ঐ যে JWT Token পেয়েছিলাম সেটা পাঠাবে। এখন সব প্রাইভেট রাউটের ক্ষেত্রে আমার এই টোকেনটা অথেনটিকেট করার প্রয়োজন হবে। তাহলে কি আমি বারবার কোড লিখবো? না, আমি কোড রিইউজ করবো। এটা করার জন্য ব্যাকএন্ডে একটা সুন্দর সিস্টেম আছে, সেটা হলো Middleware। আমরাও একটা মিডলওয়্যার বানাবো। এই মিডলওয়্যার চেক করবে যে টোকেনটা আমরা পাঠিয়েছি ওটা সার্ভারে আছে কিনা। যদি থাকে তাহলে পরবর্তী প্রসেসিং এ যাবে। আর যদি না পায় তাহলে ৪০১ এরর রিটার্ন করবে। ৪০১ মানে অথেনটিকেশন এরর। যদি সব ঠিক থাকে তাহলে ঐ ইউজার অবজেক্টের খোঁজ করবে ডাটাবেইজে। এরপর সেই ইউজারের টাইমশীট খুঁজে বের করে আনবে। এখন একই দিনের ৩ বার অ্যাটেনডেন্স দিতে পারে, ৫ বার দিতে পারবে। সেটা ডাটাবেইজ থেকে আসবে ফ্ল্যাট অ্যারে হিসেবে। সেগুলোকে আমাদের গ্রুপ করতে হবে। সবশেষে টাইমশীট রিটার্ন করবে। আমাদের যা এরর আসার সব মিডলওয়্যারের মধ্যেই হয়ে যাবে। ডাটাবেইজ থেকে এরর আসার কোনোরকম সম্ভাবনা নাই। + +এবার আমরা দেখি আমাদের অ্যাটেনডেন্স বাটন নিয়ে। + +![enable](./enable.jpg) + +যখন আমাদের বাটন এনেবল থাকবে তখন ইউজার `/attendance` রিকোয়েস্ট পাঠাবে টোকেনসহ। আগের মতো তা মিডলওয়্যারে চেক হবে। হওয়ার পর যদি টোকেন পাওয়া না যায় তাহলে এরর রিটার্ন করবে। নাহয় প্রসেসিং শুরু করবে। প্রথমে ঐ ইউজারের জন্য একটা অ্যাটেনডেন্স ক্রিয়েট করবে। ক্রিয়েট করে তা ডাটাবেইজে সেইভ করবে। সবশেষে একটা সাক্সেস ম্যাসেজ রিটার্ন করবে। + +এখন যদি বাটন ডিজেবল থাকে তখন সিস্টেম কিভাবে বুঝবে কখন ডিজেবল হবে কখন এনেবল হবে। সেটা টাইমারের সাথে তো একটা হবে। এখন একজন ইউজার একবার অ্যাটেনডেন্স দিলে পরে আর দেয়ার প্রয়োজন নেই। মানে একবার দিলে তার জন্য পরবর্তীতে বাটন ডিজেবল থাকবে। এখন সেটা কিভাবে করা যাবে। + +![disable](./disable.jpg) + +যখন টাইমার ওপেন হলে `/attendanceStatus` রিকোয়েস্ট যাবে। যথারীতি মিডলওয়্যারে টোকেন অথেনটিকেট হবে। এরপর সার্ভার ডাটাবেইজ থেকে বর্তমান অ্যাটেনডেন্স আইডি খুঁজে নিয়ে আসবে। এরপর চেক করবে ঐ ইউজার ঐ অ্যাটেনডেন্সের আন্ডারে আগে অ্যাটেন্ড করেছে কিনা? যদি করে থাকে তাহলে বাটন ডিজেবল থাকবে। আর যদি না করে থাকে তাহলে বাটন এনেবল হবে। ড্যাশবোর্ডে রিডিরেক্ট হওয়ার পর এই মিডলওয়্যারেও একটা রিকোয়েস্ট আসবে। সেটার উপর ভিত্তি করে ঐ বাটনের চেহারা পরিবর্তন হবে। এটা কেন করতে হবে? কারণ যখন বলা হবে অ্যাটেনডেন্স বাটন এনেবল করা হয়েছে এবার সবাই অ্যাটেনডেন্স দেন, তখনই আসলে সবাই লগইন করবে। লগইন হওয়ার পর যদি আবার রিফ্রেশ দিতে হয় তাহলে তো প্রব্লেম। তাই একই সাথে দুইটা রিকোয়েস্ট পাঠাতে হবে। + +## Decision Tree + +Decision Tree একটা ডায়াগ্রাম, অনেকটা ফ্লোচার্টের মতো। এটা তৈরি করে রাখলে কখনও কাউকে আর ব্যাখ্যা করে বারবার বুঝানোর দরকার পড়বে না। এটা থেকেই টিমমেটরা বুঝে যাবে। আমরা এখানে রেজিস্ট্রেশন প্রসেসের একটা Decision Tree দেখবো। + +![decision tree](./decision-tree.jpg) + +আশা করি এই ডায়াগ্রাম কাউকে বুঝিয়ে দিতে হবে না। এখান থেকে সহজেই প্রসেসটা বুঝা যাচ্ছে। এভাবে সবকিছুর জন্য যদি ডিসিশন ট্রি তৈরি করে ফেলা যায় তাহলে কেউ দেখলেই প্রসেসটা সহজেই বুঝে যাবে। + +## Project Management + +রিকোয়ারমেন্ট বেশি হলে সেগুলো যদি সঠিকভাবে ম্যানেজ করা না যায় তবে তা ট্র্যাক করা মুশকিল। কিছুদিন পর তা হারিয়ে যেতে পারে। তাই আমাদের সবকিছু ট্র্যাক রাখতে হবে। আমরা বিগিনার হিসেবে Notion ব্যবহার করতে পারি। প্রফেশনাল লাইফে অনেক প্রজেক্ট ম্যানেজমেন্ট টুলস আছে যেমনঃ Github, trello, Jira etc. সেগুলো দিয়েও করা যায়। আমরা আপাতত notion দিয়েই করছি। এই প্রজেক্টের সব রিকোয়ারমেন্টস এই [লিংক](https://thirsty-camelotia-a8e.notion.site/Projects-26859035fe2a4649b9556f5fbe77728b) সুন্দরভাবে টাস্ক আকারে আছে। আপনারা এটা থেকে একটা আইডিয়া পাবেন। + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/docs/Lectures/Fundamentals/12/Resource.md b/docs/Lectures/Fundamentals/12/Resource.md new file mode 100644 index 0000000..a9e0487 --- /dev/null +++ b/docs/Lectures/Fundamentals/12/Resource.md @@ -0,0 +1,131 @@ +## Attendance System Requirement Analysis + +## Agenda + +- Functional Requirement Analysis +- Create A Basic SRS +- Choose Technologies + +--- + +### Client's Requirement + +We need an attendance system. Students can create their own profile. Admin can see list of students and their attendances. Admin can enable and disable attend button. Also this button can be disabled based on a timer. Each time admin enable attend button, students can participate for only once. Each day, student will have a time sheet of attendance. + +Student can see their own time logs and attend button when enabled. + +--- + +### Functional Requirements + +#### Admin + +- Admin can create student. +- Admin can delete / update / check students information. +- Admin can change status of a student. +- Admin can check time sheet of a student. +- Admin can enable or disable attendance button. +- Admin can check stats of a given day. + +#### Student + +- Student can register themselves. +- There will be following account status for a student: + - Pending + - Active + - Reject +- User can login with their credentials. +- Pending and rejected users won't have anything in their profile. +- Active users can update their profile info. + - First Name + - Last Name + - Email + - Phone No + - Profile Picture +- Active user can change / update password. +- Active users can see their time sheet + - Calender view + - List view + - Table view +- Active users can participate in attendance system. +- User can logout. + +--- + +### Requirement Analysis + +#### Models + +##### User + +- Name +- Email +- Password +- Roles +- AccountStatus + +##### Profile + +- First Name +- Last Name +- Phone No +- Profile Picture +- UserId + +##### StudentAttendance + +- UserId +- CreatedAt: DateTime +- AdminAttendanceId + +##### AdminAttendance + +- CreatedAt: DateTime +- Status +- TimeLimit + +#### Endpoints + +##### Student Endpoints + +- POST /auth/login [public] +- POST /auth/register [public] +- PATCH /profiles [private] +- PATCH /profiles/avatar [private] +- PUT /auth/change-password [private] +- GET /timesheet [private] +- GET /attendance [private] +- GET /attendanceStatus [private] + +##### Admin Endpoints + +- GET /users [private] +- POST /users [private] +- PATCH /users/userId [private] +- DELETE /users/userId [private] +- GET /users/userId [private] +- GET /profiles [private] +- POST /profiles [private] +- PATCH /profiles/profileId [private] +- DELETE /profiles/profileId [private] +- GET /profiles/profileId [private] +- GET /timesheet/userId [private] +- GET /timesheet/stats [private] +- POST /attendance/enable [private] +- GET /attendance/disabled/:attendanceId [private] + +--- + +#### Visualize our workflow + +![Visualize our model](./visualize-our-model.jpg) + +--- + +#### Project Management + +See through [this notion link](https://thirsty-camelotia-a8e.notion.site/Projects-26859035fe2a4649b9556f5fbe77728b) to get an idea about project management + +#### Important links + +- [Class Overview](../../Class%20Overview/Lecture-12/README.md) diff --git a/class-overview/Lecture-12/decision-tree.jpg b/docs/Lectures/Fundamentals/12/decision-tree.jpg similarity index 100% rename from class-overview/Lecture-12/decision-tree.jpg rename to docs/Lectures/Fundamentals/12/decision-tree.jpg diff --git a/class-overview/Lecture-12/disable.jpg b/docs/Lectures/Fundamentals/12/disable.jpg similarity index 100% rename from class-overview/Lecture-12/disable.jpg rename to docs/Lectures/Fundamentals/12/disable.jpg diff --git a/class-overview/Lecture-12/enable.jpg b/docs/Lectures/Fundamentals/12/enable.jpg similarity index 100% rename from class-overview/Lecture-12/enable.jpg rename to docs/Lectures/Fundamentals/12/enable.jpg diff --git a/class-overview/Lecture-22/images/login-process.jpg b/docs/Lectures/Fundamentals/12/login-process.jpg similarity index 100% rename from class-overview/Lecture-22/images/login-process.jpg rename to docs/Lectures/Fundamentals/12/login-process.jpg diff --git a/class-overview/Lecture-22/images/registration-process.jpg b/docs/Lectures/Fundamentals/12/registration-process.jpg similarity index 100% rename from class-overview/Lecture-22/images/registration-process.jpg rename to docs/Lectures/Fundamentals/12/registration-process.jpg diff --git a/class-overview/Lecture-12/timesheet.jpg b/docs/Lectures/Fundamentals/12/timesheet.jpg similarity index 100% rename from class-overview/Lecture-12/timesheet.jpg rename to docs/Lectures/Fundamentals/12/timesheet.jpg diff --git a/resources/lecture-12/visualize-our-model.jpg b/docs/Lectures/Fundamentals/12/visualize-our-model.jpg similarity index 100% rename from resources/lecture-12/visualize-our-model.jpg rename to docs/Lectures/Fundamentals/12/visualize-our-model.jpg diff --git a/docs/Lectures/Fundamentals/13/Overview.md b/docs/Lectures/Fundamentals/13/Overview.md new file mode 100644 index 0000000..bb00a65 --- /dev/null +++ b/docs/Lectures/Fundamentals/13/Overview.md @@ -0,0 +1,297 @@ +## Create Models, Write Pseudo Code and Adda + +আজকে ক্লাসে আমরা আমাদের গত ক্লাসে Notion এ যে টুডু বানিয়েছিলাম সেটার আলোকে মডেলগুলো তৈরি করবো। আমাদের Create Models টুডু ছিল এরকম। + +![Create Models](./images/Screenshot_2.png) + +আমরা প্রথমে প্রজেক্ট স্ট্রাকচার নিয়ে একটু কথা বলি। আমরা প্রথমে আমাদের ডিরেক্টরিতে attendance-system নামে একটা ডিরেক্টরি ক্রিয়েট করবো। আমাদের এই প্রজেক্টে অ্যাপ্লিকেশন হবে দুইটা। একটা ক্লায়েন্ট আরেকটা সার্ভার। যদি আমরা Student ও Admin এর জন্য আলাদা আলাদা অ্যাপ্লিকেশন বানাতাম তাহলে আমাদের অ্যাপ্লিকেশন হতো ৩টা। এখন দুইটা দিয়ে আমাদের কাজ হয়ে যাবে। যেহেতু আমরা ক্লায়েন্ট সাইড নিয়ে এখন কাজ করবো না, ক্লায়েন্টের কোনো রিকোয়ারমেন্টস এখনও আমরা প্রোপারলি তৈরি করিনি, সুতরাং আমরা প্রথমে API নিয়ে কাজ করবো। এখন অনেকের প্রশ্ন থাকতে পারে আগে কেন ব্যাকএন্ড কেন? আমরা তো সবসময় দেখে এসেছি আগে HTML, CSS নিয়ে কাজ করে এরপর ব্যাকএন্ডে যেতাম। এখন দুনিয়াটা একটু চেইঞ্জ হয়ে গেছে। সিংগেল পেইজ অ্যাপ্লিকেশনে আগে ব্যাকএন্ডের কাজ করতে হয়। UI পরে। আবার পাশাপাশি করা যায়, যদি আমাদের কোনো টেমপ্লেট রেডি থাকতো বা অন্য একজন ফ্রন্টএন্ড ডেভেলপার থাকতো। যেহেতু এই অ্যাপ্লিকেশনে আমি একাই, তাই আগে যেটা কারো উপর ডিপেন্ড করে না সেটা বানিয়ে নিবো। সেটা হচ্ছে ব্যাকএন্ড। ফ্রন্টএন্ড ব্যাকএন্ডের উপর ডিপেন্ড করে। তাই আমরা আগে ডিপেন্ডেন্সি ক্রিয়েট করবো। এবার আমরা আমাদের attendance-system ডিরেক্টরিতে server নামে আরেকটা ডিরেক্টরি নিবো। পরবর্তীতে যখন ক্লায়েন্ট নিয়ে কাজ করবো তখন client নামে আরেকটা ডিরেক্টরি তৈরি করবো। + +nodeJS প্রজেক্ট মানেই সেখানে একটা `package.json` ফাইল থাকবে। সেটা কিভাবে ক্রিয়েট করবো। দুইভাবে করা যায়। npm or yarn. আমরা যখন nodejs ইনস্টল করবো তখন সাথে সাথে npm ও ইনস্টল হয়ে যাবে। কিন্তু yarn আমাদের আলাদা করে গ্লোবালি ইনস্টল করতে হবে। সেটা কিভাবে করতে হবে? + +```sh +npm install --global yarn +``` + +`--global` দেয়া মানে সেটা গ্লোবালি মেশিনে ইনস্টল হয়ে যাবে। আমাদের আর এই মেশিনের জন্য এটা ইনস্টল করার প্রয়োজন নেই। যদি `--global` না দিতাম তাহলে সেটা শুধু এই প্রজেক্টের জন্য ইনস্টল হতো। এবার আমরা আমাদের package.json ফাইল বানাবো। প্রথমে npm দিয়ে কিভাবে বানাবো দেখে নিই। + +```sh +npm init -y +``` + +`-y` দেয়ার মানে হলো npm আমাদের কিছু প্রশ্ন করবে, আমরা সেগুলো সবগুলোর উত্তর ইয়েস দিয়ে দিয়েছি। এই কমান্ড লিখে এন্টার দিলেই আমাদের `package.json` ফাইল তৈরি হয়ে যাবে। এবার yarn দিয়ে কিভাবে বানাবো দেখে নিই। একই কমান্ড শুধু npm এর জায়গায় yarn লিখতে হবে। + +```sh +yarn init -y +``` + +এবার এই `package.json` ফাইলের মধ্যে কি আছে একটু দেখি। + +```json +{ + "name": "server", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} +``` + +অনেক কিছুই আছে। তবে আমাদের সবচেয়ে জরুরী `scripts`, `dependencies` and `devDependencies`. dependencies এবং devDependencies এখনও দেখা যাচ্ছে না। আমরা যখন কোনো প্যাকেজ ইনস্টল করবো তখন দেখা যাবে। + +এখন আমরা কি বানাতে পারি। nodejs একটি পাওয়ারফুল টুল যার মাধ্যমে আমরা আমাদের মেশিনে আমাদের কোড রান করতে পারি। তার মানে আমরা যা খুশি তাই বানাতে পারি। আমি ডেস্কটপ অ্যাপ্লেকশনও বানাতে পারি, মোবাইল অ্যাপ্লিকেশনও বানাতে পারি যেকোনো কিছুই বানাতে পারি। কিন্তু এই মুহূর্তে আমি চাইছি ওয়েব অ্যাপ্লিকেশন বানাতে। আমরা চাইলে [NodeJS API](https://nodejs.org/api) এ দেয়া পদ্ধতি দিয়ে পুরো একটা সার্ভার বানিয়ে ফেলতে পারি। কিন্তু কেন আমরা সেটা করতে যাবো যদি আমাদের কাছে একটা ফ্রেমওয়ার্ক থাকে। nodejs এর জন্য সবচেয়ে ভাল ফ্রেমওয়ার্ক হচ্ছে [Express JS](https://expressjs.com/)। কারণ এখানে কিছু শেখার নাই। দুই তিনটা কনসেপ্ট জাস্ট যা দিয়ে আমরা সব কাজ করতে পারবো। একে বলা হয় Minimalist Web Framework বা Micro Framework। মাইক্রো ফ্রেমওয়ার্ক শুনলে অনেক ভাবে এতে বুঝি সব দেয়া নেই। মাইক্রো ফ্রেমওয়ার্কের কনসেপ্ট হলো সব কিছু ছোট আকার দেয়া আছে। আমার যখন যা দরকার তা জোড়া লাগিয়ে লাগিয়ে কাজ করবো। জ্যাঙ্গোতে সবকিছু এর মধ্যেই দেয়া আছে। আপনাকে কোনো চিন্তাই করতে হবে না। আপনার হাতে সময় কম থাকলে জ্যাঙ্গো বেস্ট। কিন্তু আপনার দরকার বেস্ট পারফরম্যান্স, বেস্ট অপটিমাইজেশন, লগইনের জন্য এই মডিউলস ভাল, অথেনটিকেশনের জন্য এই মডিউলস ভাল, ফ্লেকিবিটি দরকার, তাহলে আপনার জন্য হচ্ছে এক্সপ্রেস। এর একটা খারাপ দিক আছে। আবার এই খারাপ দিকটাই ভাল দিক। খারাপ দিক কি? বিগিনার হিসেবে স্টেপগুলো মাথায় রাখা একটু কষ্টের। আপনার হাতে টাইম কম আপনি এই স্টেপগুলো মাথায় রেখে রেখে কাজ করাটা একটু কঠিন হয়ে যায়। আবার এই স্টেওপগুলোই মাথায় রাখতে রাখতে কবে যে আপনি একজন ওয়েব ডেভেলপার হয়ে যাবেন টেরই পাবেন না। আপনি কোনো একটা কাজের জন্য চাইছে flask ইউজ করতে, আপনি পারবেন। আপনি চাইছেন GO ইউজ করতে তাও পারবেন। কারণ আপনি এক্সপ্রেশ শিখতে গিয়ে জানেন কিভাবে মিডলওয়্যার তৈরি করতে হয়, রিকোয়েস্ট রেসপন্স কি, কিভাবে হ্যান্ডেল করতে হয়, আপনি ফ্রেমওয়ার্ক নির্ভর না। আপনি দুনিয়ার যেকোনো ফ্রেমওয়ার্কে কাজ করতে পারবেন। জাস্ট ডকুমেন্টেশন পড়ে তা কিভাবে ইউজ করতে হয় সেটা বের করে নিলেই হলো। এসব কারণে এক্সপ্রেস বেস্ট। সে আমাকে ফ্লেক্সিবিলিটি দিচ্ছে। + +এখন থেকে আমরা yarn ইউজ করবো। কারণ তা ক্যাশে অনেক কিছু জমা করে রাখে, যার ফলে পরে ইনস্টল করতে গেলে সময় কম লাগে। আর এটা npm এর চেয়ে অনেক ফাস্ট। এবার আমরা express ইনস্টল করে ফেলি। + +```sh +yarn add express +``` + +একটা কথা মাথায় রাখবেন একটা প্রজেক্টে কখনও npm এবং yarn ইউজ করবেন না। কারণ যখন আপনি ডেপ্লয় করতে যাবে তখন দুইটা প্যাকেজ ফাইল থাকলে তা কনফ্লিকশন তৈরি করবে। তাই যদি npm ইউজ করেন পুরো সিস্টেমে npm, আর yarn হলে yarn। + +এবার আমাদের সার্ভার বানাতে হবে। সার্ভার মানে যে সার্ভ করে। অনেকটা রেস্টুরেন্টের ওয়েটারের মতো। আপনি ওকে অর্ডার দিবেন, ও সেটা শেফের কাছে নিয়ে যাবে, এরপর খাবার রেডি হলে আপনাকে এসে সার্ভ করবে। সার্ভার হলো অ্যাপ্লিকেশনের এমন একটা সিস্টেম যে রিকোয়েস্টগুলো শুনতে পারবে। আমরা আমাদের server ডিরেক্টরিতে server.js নামে একটা ফাইল ক্রিয়েট করবো। + +আমরা এই ফাইলের মধ্যে প্রথমে express import করে আনবো। + +```js +const express = require('express'); +``` + +express হলো একটা ফাংশন যা আমাদের Express কনস্ট্রাক্টর রিটার্ন করে। এটা আমাদের অ্যাপ্লিকেশন তৈরিতে সাহায্য করে। কিভাবে? + +```js +const app = express(); +``` + +আমরা express() কল করার মাধ্যমে যে অ্যাপ্লিকেশন পেলাম তা app এর মধ্যে রাখলাম। এই app অনেক পাওয়ারফুল। আমাদের যা যা দরকার সব এটার মধ্যে আছে। এখন এটা এভাবে লিখে রাখলে তা কিছুই শো করবে না। যদি আমরা নিচের কমান্ড লিখে রান করি দেখবো কিছুই শো করছে না। + +```sh +node server.js +``` + +যদি শো করাতে হয় তাহলে তাকে আগে রিকোয়েস্ট শুনতে হবে। এখন সে তো আর সব জায়গার রিকোয়েস্ট শুনতে পারবে না। তাহলে আমাদের একটা পোর্ট নাম্বার দিয়ে দিতে হবে ধরুন `4000`। এই পোর্ট থেকে যা রিকোয়েস্ট আসবে সব সে listen করবে। পোর্ট নাম্বার দেয়ার পর একটা কলব্যাক ফাংশন দিবো। যেখানে আমরা আপাতত এটা যে শুনছে তা কনসোলে লগ করে দেখবো। + +```js +app.listen(4000, () => { + console.log("I'm listening on port 4000"); +}); +``` + +এবার যদি server.js রান করি তাহলে দেখা যাবে সে আর থেমে যাচ্ছে না। চলছে। এখন যদি আমরা ব্রাউজারে গিয়ে localhost:4000 লিখে সার্চ দিই দেখবো একটা লেখা আসবে Cannot get /. মানে সে রাউট পাচ্ছে না। কিন্তু কোনো এররও দিচ্ছে না। এখন যদি আমরা টার্মিনাল ক্লোজ করে ব্রাউজারে রান করি আমাকে এরর দেখাবে যে সে সাইটটা খুঁজে পাচ্ছে না। তার মানে উপরে যে কোড লিখেছি সেই কোডগুলোর অনেক ভ্যালু। + +এখন আমাদের Cannot get / এই ইস্যুটা নিয়ে কাজ করা যাক। আমরা গত ক্লাসে যে রাউটগুলো বানিয়েছিলাম সেরকম রাউট শুধুমাত্র '/' এর জন্য বানাতে হবে। সেটা বানাতে পারে আমাদের app. কিভাবে? + +```js +app.get('/', (req, res) => { + res.send('Thanks for your request'); +}); +``` + +app আমাদের থেকে একটা get রিকোয়েস্ট নিবে। আর একটা কলব্যাক ফাংশন রিটার্ন করবে। এই ফাংশনের মধ্যে থাকবে তিনটা জিনিস। req, res, next. আমাদের আপাতত next দরকার নেই। আমরা res নিয়ে কাজ করবো। এখন আমরা যদি / রাউট পাঠাই তাহলে সে আমাদের রেসপন্স সেন্ড করবে। কি সেন্ড করবে? আমরা যা চাই। আমরা চাই কেউ / এ হিট করলে Thanks for your request এই লাইনটা দেখাবে। + +এখন যদি আমাদের ব্রাউজারে গিয়ে চেক করি তাহলে কিছুই পাবো না। কারণ আমি ফাইল চেইঞ্জ করেছি। সার্ভার রিস্টার্ট দিইনি। প্রতিবার ফাইল চেইঞ্জ করার পর সার্ভার রিস্টার্ট দিতে হবে। এটা অত্যন্ত প্যারাদায়ক কাজ। সেই সমস্যা থেকে উত্তোরণের জন্য আমরা nodemon নামে একটা প্যাকেজ ইউজ করবো। + +```sh +yarn add -D nodemon +``` + +-D মানে হলো এটা devDependencies এ সেইভ হবে। এটার সাথে আমাদের প্রজেক্টের কোনো সম্পর্ক নেই। আমরা আমাদের ডেভেলপমেন্ট সহজ করার জন্য এটা ইউজ করছি। আর যেগুলো প্রজেক্টের সাথে রিলেটেড সেগুলোর ক্ষেত্রে আমরা -D দিবো না। ইনস্টল করার পর আমাদের package.json এ গিয়ে "scripts" এ একটা লাইন যোগ করতে হবে। সেটা হলো + +```json +{ + "scripts": { + "dev": "nodemon server.js" + }, +} +``` + +এবার যদি আমরা `yarn dev` লিখে কমান্ড লাইনে রান করি তাহলে ফাইল যতবার চেইঞ্জ করি আমাদের আর সার্ভার রিস্টার্ট দেয়ার প্রয়োজন পড়বে না। + +আমরা এবার ব্রাউজারে গিয়ে localhost:4000 রান করলে দেখবো আমাদের জন্য Thanks for your request এই লাইনটা দেখাছে। এটা আর অন্য কোনো রাউটের ক্ষেত্রে দেখাবে না। আমরা যদি / এর পরে অন্য কিছু লিখি এটা আমাদের বলবে Cannot get অমুক রাউট। + +আমাদের Thanks for your request এর পরিবর্তে যা খুশি আমরা সেটা পাঠাতে পারি। html, css, json, video, audio, pdf যা খুশি। আমরা একটু html পাঠিয়ে দেখি। + +```js +app.get('/', (req, res) => { + res.send(`

Thanks for your request

`); +}); +``` + +ব্রাউজারে রান করলে দেখবো তা একটা h1 ট্যাগ রিটার্ন করছে। + +আমরা যেহেতু API বানাচ্ছি তাই চিন্তা করবো json পাঠানোর জন্য। + +```js +app.get('/', (req, res) => { + const obj = { + name: 'Ayman', + email: 'ayman@example.com', + }; + res.json(obj); +}); +``` + +যদি ব্রাউজারে রান করি তাহলে আমাদের অবজেক্টটা json আকারে দেখাবে। তো আমাদের বেসিক ওয়েব সার্ভার রেডি। + +এবার ধরে নিলাম আমাদের অ্যাপ্লিকেশনে ডাটাবেইজ আছে। মডেলের সাথে ডাটাবেইজের কমিউকেশন করার জন্য একটা সুন্দর ORM (Object Relational Mapping) আছে যার নাম [Mongoose](https://mongoosejs.com/). যদি আমরা mongodb নিয়ে কাজ করতে চাই তাহলে mongoose ইউজ করবো। আমরা mongoose ইনস্টল করে ফেলি। + +```sh +yarn add mongoose +``` + +এবার আমরা আমাদের মডেল তৈরি করবো। আমরা আমাদের server ডিরেক্টরির মধ্যে আরেকটা ডিরেক্টরি তৈরি করবো যেটার নাম models. এই models এর মধ্যে মডেল ফাইলগুলো তৈরি করবো যাদের নাম User.js, Profile.js, AdminAttendance.js, StudentAttendance.js। + +প্রথমে আমরা User মডেল নিয়ে কাজ করবো। গত ক্লাসে দেখেছিলাম আমাদের ইউজারের মধ্যে কি কি থাকবে। তা হলো + +```txt +- Name +- Email +- Password +- Roles +- AccountStatus +``` + +মmongoose দিয়ে মডেল তৈরি করার জন্য আমাদের প্রথম দরকার হয় একটা Schema. Schema হচ্ছে একটা শেইপ, একটা চেহারা। যেমন User মডেলে যা যা থাকবে সেটাই তার শেইপ, এর বাইরে কিছু থাকতে পারবে না। প্রথমে আমরা আমাদের mongoose import করে নিয়ে আসি। আমার সব দরকার নেই। আমার দরকার শুধু model আর Schema. + +```js +const { model, Schema } = require('mongoose'); +``` + +এবার আমরা আমাদের Schema তৈরি করে নিই। + +```js +const userSchema = new Schema({ + name: String, + email: String, + password: String, + roles: [String], + accountStatus: String, +}); +``` + +এখানে আমরা যে টাইপগুলো লিখেছি তা mongoose এর ডকুমেন্টেশন থেকে আপনারা পড়ে নিলে জানতে পারবেন। roles এর ক্ষেত্রে মাল্টিপল রোল হতে পারে যা স্ট্রিং একটা অ্যারে। আর বাকিগুলো সব স্ট্রিং। এবার আমরা আমাদের মডেল তৈরি করবো। + +```js +const User = model('User', userSchema); +``` + +প্রথম আর্গুমেন্ট হিসেবে ভ্যারিয়েবলে যে নাম দিবো সেটা স্ট্রিং আকারে দিতে হবে। দ্বিতীয় আর্গুমেন্টে দিতে হবে আমাদের Schema যেটা আমরা তৈরি করেছিলাম। এরপর আমরা আমাদের মডেলকে এক্সপোর্ট করে দিবো যাতে এই মডেলটা আমরা পুরো প্রজেক্টে যেকোনো জায়গায় ব্যবহার করতে পারি। + +```js +module.exports = User; +``` + +এখানে একটা প্রব্লেম আছে। কারণ কোনো ভ্যালিডেশন নাই। ভ্যালিডেশন আমরা পরবর্তীতে শিখবো। আজকের ক্লাসের কনটেক্সটের বাইরে সেটা। + +এবার আমরা আমাদের প্রোফাইল মডেল তৈরি করি। + +```js +const { model, Schema } = require('mongoose'); + +const profileSchema = new Schema({ + firstName: String, + lastName: String, + phone: String, + avatar: String, + user: { + type: Schema.Types.ObjectId, + ref: 'User', + }, +}); + +const Profile = model('Profile', profileSchema); + +module.exports = Profile; +``` + +সব আগের মতোই। কিন্তু user ডিফারেন্ট টাইপের। এটা হলো আমাদের প্রোফাইলটা কার সেটা বের করতে হবে। সেটা বের করার জন্য mongoose এ এই স্পেশাল টাইপের ডাটা ব্যবহার করতে হবে। + +এবার আমরা আমাদের Admin Attendance ও Student Attendance মডেল তৈরি করবো। + +```js +// AdminAttendance.js +const { Schema, model } = require('mongoose'); + +const adminAttendanceSchema = new Schema({ + timeLimit: Number, + status: String, + createdAt: Date, +}); + +const AdminAttendance = model('AdminAttendance', adminAttendanceSchema); + +module.exports = AdminAttendance; +``` + +```js +// StudentAttendance.js + +const { Schema, model } = require('mongoose'); + +const studentAttendanceSchema = new Schema({ + createdAt: Date, + user: { + type: Schema.Types.ObjectId, + ref: 'User', + }, + adminAttendance: { + type: Schema.Types.ObjectId, + ref: 'AdminAttendance', + }, +}); + +const StudentAttendance = model('StudentAttendance', studentAttendanceSchema); + +module.exports = StudentAttendance; +``` + +আমাদের টুডু থেকে Create Models টাস্ক শেষ। এরপর আমরা Authentication শুরু করবো। তার আগে ব্যাকএন্ডের ৭টা ক্লাস দেয়া হবে। ওখানে এক্সপ্রেশ নিয়ে খুঁটিনাটি সব দেখানো আছে। সেগুলো শেষ করে সব সুডোকোড নিজে থেকে লেখার চেষ্টা করবেন। উদাহরণ হিসেবে দুইটা সুডোকোড দেখানো হলো। + +**Registration Process:** + +```txt +Start +name = input() +email = input() +password = input() +if name && email && password is invalid: + return 400 error + +user = find user with email +if user found: + return 400 error + +hash = hash password +user = save name, email, hash to user model +return 201 +End +``` + +**Login Process:** + +```txt +Start +email = input() +password = input() + +user = find user with email +if user not found: + return 400 error + +if password not equal to user hash: + return 400 error + +token = generate token using user +return token +End +``` + + + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/docs/Lectures/Fundamentals/13/Resource.md b/docs/Lectures/Fundamentals/13/Resource.md new file mode 100644 index 0000000..073ca0d --- /dev/null +++ b/docs/Lectures/Fundamentals/13/Resource.md @@ -0,0 +1,55 @@ +## Create Models, Write Pseudo Code and Adda + +## Pseudo code + +### Authentication + +**Registration Process:** + +```txt +Start +name = input() +email = input() +password = input() +if name && email && password is invalid: + return 400 error + +user = find user with email +if user found: + return 400 error + +hash = hash password +user = save name, email, hash to user model +return 201 +End +``` + +**Login Process:** + +```txt +Start +email = input() +password = input() + +user = find user with email +if user not found: + return 400 error + +if password not equal to user hash: + return 400 error + +token = generate token using user +return token +End +``` + +--- + +**Progress:** +Follow [this link](https://thirsty-camelotia-a8e.notion.site/Attendance-System-8b5ccfe9b2384e84b904d6a85013170b) to check the progress + +**Source Code** +[Click Here](../../src/lecture-13/) + +**Class Overview:** +[Lecture 13](../../Class%20Overview/Lecture-13/README.md) diff --git a/docs/Lectures/Fundamentals/13/SourceCode.md b/docs/Lectures/Fundamentals/13/SourceCode.md new file mode 100644 index 0000000..fe42206 --- /dev/null +++ b/docs/Lectures/Fundamentals/13/SourceCode.md @@ -0,0 +1 @@ +## Your Source Code Is On this [link](github.com) \ No newline at end of file diff --git a/src/attendance-system/server/models/AdminAttendance.js b/docs/Lectures/Fundamentals/13/attendance-system/server/models/AdminAttendance.js similarity index 100% rename from src/attendance-system/server/models/AdminAttendance.js rename to docs/Lectures/Fundamentals/13/attendance-system/server/models/AdminAttendance.js diff --git a/src/attendance-system/server/models/Profile.js b/docs/Lectures/Fundamentals/13/attendance-system/server/models/Profile.js similarity index 100% rename from src/attendance-system/server/models/Profile.js rename to docs/Lectures/Fundamentals/13/attendance-system/server/models/Profile.js diff --git a/src/attendance-system/server/models/StudentAttendance.js b/docs/Lectures/Fundamentals/13/attendance-system/server/models/StudentAttendance.js similarity index 100% rename from src/attendance-system/server/models/StudentAttendance.js rename to docs/Lectures/Fundamentals/13/attendance-system/server/models/StudentAttendance.js diff --git a/src/lecture-13/attendance-system/server/models/User.js b/docs/Lectures/Fundamentals/13/attendance-system/server/models/User.js similarity index 100% rename from src/lecture-13/attendance-system/server/models/User.js rename to docs/Lectures/Fundamentals/13/attendance-system/server/models/User.js diff --git a/src/lecture-13/attendance-system/server/package.json b/docs/Lectures/Fundamentals/13/attendance-system/server/package.json similarity index 100% rename from src/lecture-13/attendance-system/server/package.json rename to docs/Lectures/Fundamentals/13/attendance-system/server/package.json diff --git a/src/lecture-13/attendance-system/server/server.js b/docs/Lectures/Fundamentals/13/attendance-system/server/server.js similarity index 100% rename from src/lecture-13/attendance-system/server/server.js rename to docs/Lectures/Fundamentals/13/attendance-system/server/server.js diff --git a/class-overview/Lecture-13/images/Screenshot_2.png b/docs/Lectures/Fundamentals/13/images/Screenshot_2.png similarity index 100% rename from class-overview/Lecture-13/images/Screenshot_2.png rename to docs/Lectures/Fundamentals/13/images/Screenshot_2.png diff --git a/docs/assets/fullstack.png b/docs/assets/fullstack.png new file mode 100644 index 0000000..8cee477 Binary files /dev/null and b/docs/assets/fullstack.png differ diff --git a/docs/assets/stack.gif b/docs/assets/stack.gif new file mode 100644 index 0000000..95ff3e5 Binary files /dev/null and b/docs/assets/stack.gif differ diff --git a/class-overview/Lecture-01/Waterfall_model.png b/docs/blog/articles/Waterfall_model.png similarity index 100% rename from class-overview/Lecture-01/Waterfall_model.png rename to docs/blog/articles/Waterfall_model.png diff --git a/class-overview/Lecture-01/agile-model-of-se.png b/docs/blog/articles/agile-model-of-se.png similarity index 100% rename from class-overview/Lecture-01/agile-model-of-se.png rename to docs/blog/articles/agile-model-of-se.png diff --git a/articles/application-requirements-and-landscape/README.md b/docs/blog/articles/application-requirements-and-landscape.md similarity index 100% rename from articles/application-requirements-and-landscape/README.md rename to docs/blog/articles/application-requirements-and-landscape.md diff --git a/class-overview/Lecture-01/devops.png b/docs/blog/articles/devops.png similarity index 100% rename from class-overview/Lecture-01/devops.png rename to docs/blog/articles/devops.png diff --git a/articles/understand-programming-languages/README.md b/docs/blog/articles/understand-programming-languages.md similarity index 100% rename from articles/understand-programming-languages/README.md rename to docs/blog/articles/understand-programming-languages.md diff --git a/articles/we-need-freedom/README.md b/docs/blog/articles/we-need-freedom.md similarity index 100% rename from articles/we-need-freedom/README.md rename to docs/blog/articles/we-need-freedom.md diff --git a/articles/README.md b/docs/blog/index.md similarity index 100% rename from articles/README.md rename to docs/blog/index.md diff --git a/docs/hosting-and-deployment/aws-amplify-console.md b/docs/hosting-and-deployment/aws-amplify-console.md new file mode 100644 index 0000000..02c161f --- /dev/null +++ b/docs/hosting-and-deployment/aws-amplify-console.md @@ -0,0 +1,18 @@ +# Host on AWS Amplify Console + +[![amplifybutton](https://oneclick.amplifyapp.com/button.svg)](https://console.aws.amazon.com/amplify/home#/deploy?repo=https://github.com/peaceiris/mkdocs-material-boilerplate) + +- [AWS Amplify Console] + +You can use **Password protection** each branch. + +Use the following build specification YML file. + +- [mkdocs-material-boilerplate/amplify.yml] + + + + + +[AWS Amplify Console]: https://aws.amazon.com/amplify/console/ +[mkdocs-material-boilerplate/amplify.yml]: https://github.com/peaceiris/mkdocs-material-boilerplate/blob/main/amplify.yml diff --git a/docs/hosting-and-deployment/combinations.md b/docs/hosting-and-deployment/combinations.md new file mode 100644 index 0000000..3f972d7 --- /dev/null +++ b/docs/hosting-and-deployment/combinations.md @@ -0,0 +1,42 @@ +# Hosting and Deployment + + + +## GitHub Pages and GitHub + +- Host source code on GitHub. +- Build and deploy with: + - `mkdocs gh-deploy` + - GitHub Actions + + + +## GitLab Pages and GitLab + +- Host source code on GitLab. +- Build and deploy with GitLab CI/CD. + + + +## Netlify + +Host source code on: + +- GitHub +- GitLab +- BitBucket + +Build and deploy with Netlify. + + + +## AWS Amplify Console + +Host source code on: + +- GitHub +- GitLab +- BitBucket +- AWS CodeCommit + +Build and deploy with AWS Amplify Console. diff --git a/docs/hosting-and-deployment/github-pages.md b/docs/hosting-and-deployment/github-pages.md new file mode 100644 index 0000000..cd7f0fc --- /dev/null +++ b/docs/hosting-and-deployment/github-pages.md @@ -0,0 +1,34 @@ +# Host on GitHub Pages + +- [Demo site on GitHub Pages] (build & deploy with GitHub Actions) + + + +## Build and deploy with GitHub Actions + +- [peaceiris/actions-gh-pages: GitHub Actions for deploying to GitHub Pages with Static Site Generators](https://github.com/peaceiris/actions-gh-pages) + +Go to the repository and read the latest `README.md` for more details. + + + +## Build and deploy with `mkdocs gh-deploy` + +### pipenv + +``` +pipenv run deploy + +# OR +pipenv shell +mkdocs gh-deploy + +# OR +pipenv run mkdocs gh-deploy +``` + + + + + +[Demo site on GitHub Pages]: https://peaceiris.github.io/mkdocs-material-boilerplate/ diff --git a/docs/hosting-and-deployment/gitlab-pages.md b/docs/hosting-and-deployment/gitlab-pages.md new file mode 100644 index 0000000..5f8e173 --- /dev/null +++ b/docs/hosting-and-deployment/gitlab-pages.md @@ -0,0 +1,3 @@ +# Host on GitLab Pages + +- See [.gitlab-ci.yml](https://github.com/peaceiris/mkdocs-material-boilerplate/blob/main/.gitlab-ci.yml) diff --git a/docs/hosting-and-deployment/netlify.md b/docs/hosting-and-deployment/netlify.md new file mode 100644 index 0000000..13e233a --- /dev/null +++ b/docs/hosting-and-deployment/netlify.md @@ -0,0 +1,13 @@ +# Host on Netlify + +- [Demo site on Netlify] (build & deploy with Netlify) + +Create GitHub repository and deploy to Netlify with the following button in 1 min. + +[![Deploy to Netlify from GitHub](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/Taraldinn/mkdocs-material-boilerplate) + + + + + +[Demo site on Netlify]: https://mkdocs-material.netlify.com/ diff --git a/docs/images/graduate-cap.192x192.png b/docs/images/graduate-cap.192x192.png new file mode 100644 index 0000000..5e78a04 Binary files /dev/null and b/docs/images/graduate-cap.192x192.png differ diff --git a/docs/images/graduate-cap.48x48.png b/docs/images/graduate-cap.48x48.png new file mode 100644 index 0000000..cd824fd Binary files /dev/null and b/docs/images/graduate-cap.48x48.png differ diff --git a/docs/images/graduate-cap.96x96.png b/docs/images/graduate-cap.96x96.png new file mode 100644 index 0000000..18484b8 Binary files /dev/null and b/docs/images/graduate-cap.96x96.png differ diff --git a/docs/images/graduate-cap.png b/docs/images/graduate-cap.png new file mode 100644 index 0000000..ff9f456 Binary files /dev/null and b/docs/images/graduate-cap.png differ diff --git a/docs/images/material.png b/docs/images/material.png new file mode 100644 index 0000000..7df39e0 Binary files /dev/null and b/docs/images/material.png differ diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..ae92aab --- /dev/null +++ b/docs/index.md @@ -0,0 +1,93 @@ +# FullStack Army - The Most Advanced MERN Stack Resources + +FullStack Army The Most Advanced MERN Stack Resources.png + +**Documentation: https://fullstackarmy.taraldinn.me/** + +**Source Code: https://github.com/mrhm-dev/full-stack-army** + + + +???+ note + + In this repository, you will find source codes, explanations, and other resources for **FullStack Army - Complete MERN Stack Course in Bangla**. It is an open-source repository and feels free to add documentation, examples, and other references when necessary. Please create issues when you get confused or if something does not work. Other members will help you to solve your issues. + + +## Quick Links + +- [Live Classes](live-classes/README.md) +- [Articles](articles/README.md) +- [Examples](examples/README.md) +- [Projects](projects/README.md) +- [References](references/README.md) +- [Resources](resources/README.md) +- [Source Code](src/README.md) +- [Class Overview](Class Overview/README.md) + + + + +## Live Class Recording + + +- [0. Full-stack Army - Welcome | Decision-Making Video](https://www.youtube.com/watch?v=ewBBT6Iph0M&t=784s) +- [Lecture 1 - Application Requirements & Landscape | Development Big Picture](https://youtu.be/AnD2KuKNsxE) +- [Lecture 2 - We Need Freedom, We have to Stop Technology War](https://youtu.be/9ltapy7kK5w) +- [Lecture 3 - Programming Language Foundation - A Bigger Landscape](https://youtu.be/1LWq-OdB7jY) +- [Lecture 4 - Programming Fundamentals using JavaScript](https://youtu.be/tAkUXTvm-xo) +- [Lecture 5 - Array Operations - Imperative vs Declarative](https://youtu.be/LADJO7KniNY) +- [Lecture 6 - JavaScript Array and Object Deep Dive](https://youtu.be/Mr5rksCjybA) +- [Lecture 7 - QNA 1 - Don't Miss The Last Part](https://youtu.be/TAa7gSbPVis) +- [Lecture 8 - Understand JavaScript Functions | Function as a value](https://youtu.be/lctjTl1ftdw) +- [Lecture 9 - Functional Programming in JavaScript](https://youtu.be/wMy2IZ12mxM) +- [Lecture 10 - Asynchronous Programming in JavaScript](https://youtu.be/OCkxS7W3gwU) +- [Lecture 11 - Async Iterator & Generator in JavaScript | Project Requirements](https://youtu.be/phzeyHwoIrQ) +- [Lecture 12 - Attendance System Requirement Analysis](https://youtu.be/Gsj7uU_7Um4) +- [Lecture 13 - Create Models, Write Pseudo Code and Adda](https://youtu.be/BfGOYh9Fdwg) +- [Lecture 14 - Backend 1 | Course planning and discussion](https://youtu.be/QBTOAGGgehA) +- [Lecture 15 - [Backend 2] Introduction to Backend Development](https://youtu.be/Mc6UEF957hU) +- [Lecture 16 - [Backend 3] Understand Express Middleware](https://youtu.be/kXeNJJ4mQ7w) +- [Lecture 17 - [Backend 4] Raffle Draw Project](https://youtu.be/4D2DIu8bhqU) +- [Lecture 18 - [Backend 5] Understand The Concepts of Database](https://youtu.be/SyKO3oZLz00) +- [Lecture 19 - [Backend 6] Adda with Random Topics | You can Skip](https://youtu.be/mqq5VgRMIho) +- [Lecture 20 - [Backend 7] Start Working with Mongoose](https://youtu.be/y5Rism0fEqE) +- [Lecture 21 - QNA on Express 101 and Books](https://youtu.be/krI6QUCGHY4) +- [Lecture 22 - Authentication System from Pseudo Code to Real Code](https://youtu.be/0gl4grplEcI) +- [Lecture 23 - Implement JWT and Refactor The Project Structure](https://youtu.be/D5A5BSGQVBU) +- [Lecture 24 - Implement User CRUD Operations](https://youtu.be/xr0sKPvAipQ) +- [Lecture 25 - QNA on 5 Recorded Courses and Motivational ADDA](https://youtu.be/KseSdSmvvuM) +- [Lecture 26 - Implement Attendance System Main Functionalities](https://youtu.be/A5S7mWxqs2s) +- [Lecture 27 - Frontend Core Concepts and Communication](https://youtu.be/0T7YagglhFY) +- [Lecture 28 [Frontend 1] - Frontend Course Planning & Discussion](https://youtu.be/FppAFtsxICk) +- [Lecture 29 [Frontend 2] - Understand React in A Different Way](https://youtu.be/vmw-sSTFwAk) + + + +## Add Necessary References + +Feel free to update this `README.md` file to add additional resources like youtube videos, youtube playlists, articles, books, and other references. + +## Youtube Videos + +- `CLI` [Command Line Interface For Beginners](https://youtu.be/xF6t9h8iD6I) + +**Youtube Playlists:** + +- `Git` [Git Bangla Tutorial](https://www.youtube.com/playlist?list=PL_XxuZqN0xVDDw5eyzuRDXBzgdnW7UpDF) +- `Development` [Development Essentials - Must Learn](https://www.youtube.com/playlist?list=PL_XxuZqN0xVAebtxbmfZUaq69AS3ST4RZ) +- `JavaScript` [JavaScript All You Need to Know](https://www.youtube.com/playlist?list=PL_XxuZqN0xVAu_dWUVFbscqZdTzE8t6Z1) +- `JavaScript` [Make Fun of JavaScript Array](https://www.youtube.com/playlist?list=PL_XxuZqN0xVDr08QgQHljCecWtA4jBLnS) + +**Articles:** + +**Books:** + +- `FullStack` [FullStack Development - Connecting The Dots](https://www.rokomari.com/book/211527/fullstack-development) + +**Others:** + +## Our Contributors + + + + diff --git a/references/README.md b/docs/references.md similarity index 100% rename from references/README.md rename to docs/references.md diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css new file mode 100644 index 0000000..60d97a3 --- /dev/null +++ b/docs/stylesheets/extra.css @@ -0,0 +1,16 @@ +@font-face { + font-family: "RuhulAminX"; + src: url("https://res.cloudinary.com/aldinn/raw/upload/v1654881987/blog/Ruhul-Amin-Unicode.woff2"); + } + @font-face { + font-family: "Kohinoor"; + src: url("https://res.cloudinary.com/aldinn/raw/upload/v1655044285/blog/KohinoorBangla-Medium.woff2"); + + } + :root { + + --md-text-font-size: 2rem; + + } + + \ No newline at end of file diff --git a/full-stack-army/.gitignore b/full-stack-army/.gitignore new file mode 100644 index 0000000..95ab85f --- /dev/null +++ b/full-stack-army/.gitignore @@ -0,0 +1,143 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix diff --git a/full-stack-army/README.md b/full-stack-army/README.md new file mode 100644 index 0000000..382d210 --- /dev/null +++ b/full-stack-army/README.md @@ -0,0 +1,20 @@ +# FullStack Army - The Most Advanced MERN Stack Resources + +In this repository, you will find source codes, explanations, and other resources for **FullStack Army - Complete MERN Stack Course in Bangla**. It is an open-source repository and feels free to add documentation, examples, and other references when necessary. Please create issues when you get confused or if something does not work. Other members will help you to solve your issues. + +## Quick Links + +- [Live Classes](live-classes/README.md) +- [Articles](articles/README.md) +- [Examples](examples/README.md) +- [Projects](projects/README.md) +- [References](references/README.md) +- [Resources](resources/README.md) +- [Source Code](src/README.md) +- [Class Overview](./class-overview/README.md) + +### Our Contributors + + + + diff --git a/full-stack-army/articles/README.md b/full-stack-army/articles/README.md new file mode 100644 index 0000000..6d88308 --- /dev/null +++ b/full-stack-army/articles/README.md @@ -0,0 +1,26 @@ +# Community Driven Articles + +Write and share your valuable articles for our community. You can write article in both Bengali or English. But Bengali is most preferable for our audiences. You can write single or series articles. To share your article please follow the instructions below. + +**Instructions:** + +- Create a directory inside `articles` for your new article. Give it a proper name for your article or series. +- Put all necessary assets inside your `asset` directory. +- For single article you can simply create a `README.md` file. Share author information at the end of the article. +- For series article use `README.md` file to share your table of contents, basic overview of the series and author information. +- After your pull request being merged make sure you also update `articles/README.md` file with your newly created article link. + +--- + +**Important Articles:** + +_Here we will pin the most important and valuable articles_ + +**Article Series:** +_List of all series articles_ + +**Articles:** + +- `Programming` [Understand Programming Languages](understand-programming-languages/README.md) +- [Application Requirements and Landscape](./application-requirements-and-landscape/README.md) +- [We Need Freedom, We have to Stop Technology War](./we-need-freedom/README.md) diff --git a/full-stack-army/articles/application-requirements-and-landscape/README.md b/full-stack-army/articles/application-requirements-and-landscape/README.md new file mode 100644 index 0000000..577c0a7 --- /dev/null +++ b/full-stack-army/articles/application-requirements-and-landscape/README.md @@ -0,0 +1,212 @@ +# Application Requirements and Landscape + +## SRS (Software Requirements Specifications) + +ধরে নিলাম আমরা প্রত্যেকেই একেকজন উদ্যোক্তা। আমরা একটা ব্যবসা শুরু করতে যাচ্ছি। সে ব্যবসার নাম হলো Problem Solvers Caffe (PS Caffe)। এটা মূলত যারা প্রোগ্রামার বা problem solvers তাদের জন্য একটা বিশেষ ক্যাফে আমরা শুরু করতে যাচ্ছি। + +এর requirements আছে কিছু। এই ধরণের রিকোয়ারমেন্টসকে বলা হয় SRS (Software Requirements Specifications)। এই অ্যাপের জন্য SRS হলোঃ + +> PS Caffe is an imaginary online coffee delivery service. This business is designed specially for programmers by keeping their life style in mind. We need an application to accept orders from online. The application will have the following functional and non functional requirements. +> **Special Notes:** We are not planning to grow fast. We need an [MVP (Minimal Viable Product)](https://en.wikipedia.org/wiki/Minimum_viable_product) or Workable Prototype to research our targeted market. + +খুবই সিম্পল একটা অ্যাপ্লিকেশন। আমরা প্রোগ্রামারদের লাইফস্টাইল মাথায় রেখে আমরা যেখানে আছি সেখানে একটি কফি ডেলিভারি সার্ভিস তৈরি করতে চাইছি। যেহেতু আমরা প্রোগ্রামারদের লাইফস্টাইল চিন্তায় রেখে তা করতে চাইছি তার মানে আমাদের ২৪/৭ সার্ভিস দিতে হবে। এটি একটি অনলাইনভিত্তিক সার্ভিস। একটি স্পেশাল নোট আছে যাতে লেখা আছে আমরা খুব দ্রুত ব্যবসাটাকে বাড়াতে চাইছি না। আমরা শুধু মার্কেট রিসার্চের জন্য একটি MVP (Minimal Viable Product) or Workable prototype তৈরি করতে চাইছি। এই MVP এবং রিয়েল লাইফ অ্যাপ্লিকেশনের মধ্যে অনেক বিশাল একটা পার্থক্য রয়েছে। এটা বলে নেয়া জরুরী MVP টার্মটি শুধু যে সফটওয়্যার ডেভেলপমেন্টের সাথে জড়িত এমন নয়। এর বাইরে যেকোনো ফিল্ডেই এটা ব্যবহার করা যেতে পারে এবং ব্যবহার হচ্ছে। +MVP কে আমরা অন্য ভাষায় Workable Prototype ও বলে থাকি। প্রোটোটাইপ দুই ধরণের হয়। এক ধরণের প্রোটোটাইপ যার শুধুমাত্র UI দেখা যাবে কিন্তু কোনো কাজ করা যাবে না। আর আমাদেরটা হলো ওয়ার্কেবল প্রোটোটাইপ। এর মানে আমরা এমন একটা অ্যাপ্লিকেশন বানাবো যার মাধ্যমে আমরা ২০০/৩০০ মানুষকে সার্ভিস দিতে পারবো। তাহলে প্রথম ভার্সনের জন্য অনেক ফাংশনালিটিজেরই প্রয়োজন হবে না। পরে ব্যবসা বৃদ্ধির সাথে সাথে আমরা এই জিনিসগুলোকে আপডেট করতে থাকবো। +যেহেতু আমরা MVP বানাচ্ছি এবং ভবিষ্যতে আমাদের ব্যবসা বৃদ্ধি করার কথাও মাথায় আছে, সুতরাং আমাদের খেয়াল রাখতে হবে যেন আমাদের অ্যাপ্লিকেশনটা পরবর্তীতে যেকোনো আর্কিটেকচারে সহজেই ট্রান্সফার করতে পারি সে ব্যবস্থা রাখা। +এই অ্যাপ্লিকেশনের কিছু funtional and non-functional requirements আছে। ইন্টারভিউতে গুরুত্বপূর্ণ একটি প্রশ্ন থাকে What is the difference between functional and non-functional requirements. + +### ফাংশনাল রিকোয়ারমেন্টস + +ফাংশনাল রিকোয়ারমেন্টস হলো এমন কিছু রিকোয়ারমেন্টস যা আমার অ্যাপ্লিকেশনের ফিচারে উপর ডিপেন্ড করে। এগুলো সাধারণত ক্লায়েন্ট বলে দেয়। আবার নাও বলে দিতে পারে। যেমন ক্লায়েন্ট হয়তো বললো অ্যাপ্লিকেশন লগিনের কথা। লগ আউটের কথা সে বললোই না। কিন্তু লগিন থাকলে অবশ্যই সেখানে লগ আউট থাকবেই। সুতরাং ক্লায়েন্ট বলে না দিলেও অ্যাপ্লিকেশনের খাতিরে আমাদের কিছু কিছু ফাংশনাল রিকোয়ারমেন্ট নিজেদের যুক্ত করতে হবে। এই অ্যাপ্লিকেশনের ফাংশনাল রিকোয়ারমেন্টস আছে কিছু। সেগুলো হলোঃ + +> **Functional Requirements (Mostly client requirements):** +> +> - Local Authentication: +> At the beginning we don't want to spend more on authentication services. Just keep it simple by implementing a local authentication using email & password. But make sure, we can extend local AUTH to OAuth2 anytime in future. We need the following features - +> - Hashed password +> - Email verification +> - Forget password +> - Block users if necessary + +প্রথমে আমাদের কোনো ধরণের পেইড অথেনটিকেশন সার্ভিসের প্রয়োজন নেই। আমরা শুধু ইমেইল আর পাসওয়ার্ড দিয়ে লোকাল অথেনটিকেশন করবো। তবে আমাদের খেয়াল রাখতে হবে যেন ভবিষ্যতে আমরা যেন local AUTH to OAuth2 তে এক্সটেন্ড করতে পারি এই সিস্টেম রাখা। এটা আমরা যখন মডেল তৈরি করবো বা কন্ট্রোলার তৈরি করবো তখন এরকম একটা ব্যবস্থা আমাদের রাখতে হবে। এছাড়া আমাদের কিছু ফিচার রাখতে হবে যেমন - পাসওয়ার্ড হ্যাশ করা, ইমেইল ভেরিফিকেশন, ফরগেট পাসওয়ার্ড এবং যখন চাইবো তখন যেন আমরা যেকোনো ইউজারকে ব্লক করে দিতে পারি। + +> - Multiple Roles (Role Based Access Control): +> There will be mainly five roles: +> - Admin: Admin can create and manage everything including sales data +> - Manager: Manager can't be able to create anything but can see sales data, inventory and products +> - Chef: They can only see queue orders +> - Delivery Man: They can manage the queue orders and change status +> - User: won't able to see any admin information but able to check products, reviews and place orders. + +এই অ্যাপ্লিকেশনে ৫টা মেইন রোল থাকবে। অ্যাডমিন সবকিছুতে এক্সেস নিতে পারবে। ম্যানেজার শুধু সেলস ডাটা, ইনভেনটরি আর প্রোডাক্ট দেখতে পারবে। শেফ শুধু যেসব অর্ডার কিউতে আছে তা দেখতে পারবে। ডেলিভারি ম্যান অর্ডার ম্যানেজ করতে পারবে আর স্ট্যাটাস চেইঞ্জ করতে পারবে। যেমন ডেলিভারির জন্য বের হলে সে স্ট্যাটাস চেইঞ্জ করে out for delivery, ডেলিভারি হয়ে গেলে Delivered এরকম স্ট্যাটাস চেইঞ্জ করতে পারবে। ইউজার কখনও অ্যাডমিন ইনফরমেশন দেখতে পারবে না, শুধু প্রোডাক্ট চেক করতে পারবে, অর্ডার প্লেইস করতে পারবে এবং রিভিউ দিতে পারবে। + +> - User will be able to place orders + +ইউজার অর্ডার প্লেইস করতে পারবে নিজের পছন্দমতো। + +> - User will be able see existing reviews and only place review after a successful order + +ইউজার এক্সিস্টিং রিভিউগুলো দেখতে পারবে। কিন্তু নিজে ততক্ষণ পর্যন্ত রিভিউ দিতে পারবে না যতক্ষণ পর্যন্ত একটা অর্ডার দিবে না এবং সেই অর্ডারের ডেলিভারি কমপ্লিট হবে না। প্রতিটা অর্ডারের বিপরীতে শুধু সেই অর্ডারের রিভিউ দিতে পারবে। + +> - Sales dashboard + +এখান থেকে সেলস স্ট্যাটাস জানা যাবে। যে কত বিক্রি হলো, এই সপ্তাহে বেশি হলো না কম এসব ডাটা অ্যানালাইসিস করা যাবে এই ড্যাশবোর্ড থেকে। + +> - Manage inventory + +এখানে থেকে সমস্ত ইনভেনটরির ট্র্যাকিং রাখা যাবে। যেমন চিনি, কফি, দুধ ইত্যাদির মজুত কি পরিমাণ আছে তা এখানে রেকর্ড থাকবে। এছাড়াও এমন একটি ফিচার অ্যাড করা যায়, যেমন একটা ক্যাপাচিনো বানাতে কি পরিমাণ কফি, চিনি বা দুধ লাগে তা একটা অর্ডারের বিপরীতে বাদ দিয়ে বাকিটা শো করানো যায়। + +> - Live tracking of the order + +ইউজার তার অর্ডারটা লাইভ ট্র্যাকিং করতে পারবে। যেমন অর্ডার প্লেইস হলে অর্ডার প্লেইসড এরকম একটা স্ট্যাটাস দেখাবে। অনুরূপভাবে যখন কফি বানানো হচ্ছে তখন প্রসেসিং, ডেলিভারির জন্য বের হলে আউট ফর ডেলিভারি এরকম কিছু স্ট্যাটাস সে দেখতে পারবে। পরবর্তীতে অ্যাপ্লিকেশনের গ্রোথের উপর নির্ভর করে গুগল ম্যাপের API কানেক্ট করে লাইভ লোকেশন ট্র্যাকিং এর ব্যবস্থাও করা যেতে পারে। + +এই গেলো আমাদের ফাংশনাল রিকোয়ারমেন্টস। + +### নন ফাংশনাল রিকোয়ারমেন্টস + +নন ফাংশনাল রিকোয়ারমেন্টস হলো এমন কিছু রিকোয়ারমেন্টস যা অ্যাপ্লিকেশনের ফিচার কেমন হবে না হবে তার উপর ডিপেন্ড করে না। আর এই নন ফাংশনাল রিকোয়ারমেন্টসগুলো প্রায় সব অ্যাপ্লিকেশনের জন্য একই হয়। এগুলো মূলত আমাদের অ্যাপ্লিকেশন কতো সুন্দরভাবে, সেইফলি পারফর্ম করবে তা নিশ্চিত করে। যেমন এই অ্যাপ্লিকেশনের কিছু নন ফাংশনাল রিকোয়ারমেন্টস আছে। সেগুলো হলোঃ + +> **Non Functional Requirements:** + +> - Secure + +একটা অ্যাপ্লিকেশন বানাতে গেলে অবশ্যই তা সিকিউর হতে হবে। আমরা কেউই চাই না আমার অ্যাপ্লিকেশনে যে কেউ ঢুকে যা খুশি তা করে ফেলবে। তাই প্রয়োজন অনুসারে অ্যাপ্লিকেশন সিকিউর করতে হবে। + +> - Reliable + +অবশ্যই আমাদের অ্যাপ্লিকেশন এমন হতে হবে যেন সবাই rely করতে পারে। ধরুন কেউ একটা কফি অর্ডার করলো। ৩০ মিনিট পর ঢুকে দেখলো তার ড্যাশবোর্ড থেকে অর্ডার উধাও। কিন্তু তার টাকা কেটে ফেলেছে। এরকম হলে কেউই এই অ্যাপ্লিকেশনের উপর রিলাই করতে পারবেনা। সুতরাং আমাদের Data secure হতে হবে, reliable হতে হবে এবং Data consistency বজায় রাখতে হবে। + +> - Easy Maintainability + +MVP এমন একটি প্রোডাক্ট, যে প্রোডাক্ট আমরা পাবলিক রিসার্চের জন্য বানিয়েছি। সুতরাং এতে অনেক বাগ থাকবে এটা নিশ্চিত। এছাড়াও দিনে দিনে কাস্টমারের অনেক রিকোয়ারমেন্ট বাড়তে পারে। তারা ব্যবহার করে আমাদের রিকোয়ারমেন্ট দিলো যে এই ফিচারটা হলে ভাল হয়। আমরাও ব্যবসার ভালর জন্য দেখলাম কিছু ফিচার যোগ করলে ব্যবসা আরো ভাল হবে। এসব দিক মাথায় রেখে অবশ্যই আমাদের অ্যাপ্লিকেশনটি বানাতে হবে যেন প্রয়োজনে যেকোনো ফিচার সহজে অ্যাড করা যায়, সহজে যেকোনো বাগ ফিক্স করা যায়। + +> - Awesome Usability + +এটি পুরোপুরি একটি UX টার্ম। আমার অ্যাপ্লিকেশন যদি ইউজার সহজে ইউজ করতে না পারে তাহলে তা যত ভাল ফিচার্সই দিকনা কেন তা কেউই ইউজ করবে না। সুতরাং Usability অবশ্যই ভাল হতে হবে। + +> - High availability (not main concern for MVP) + +এর মানে হলো দুনিয়াতে যাই ঘটে যাক আমাদের অ্যাপ্লিকেশন ২৪/৭ অ্যাভাইলেবল থাকতে হবে। এখানে একটা কনসেপ্ট আছে - ইলেভেন নাইন (৯৯.৯৯৯৯৯৯৯৯৯%)। এর মানে হচ্ছে সবথেকে বেশি অ্যাভেইলেবল। এর চেয়ে বেশি অ্যাভেইলেবল হওয়া সম্ভব না। এগুলো মূলত ক্লাউড নিয়ে যারা কাজ করেন তারা এর সাথে পরিচিত। আমাদের খুঁজে বের করতে হবে অ্যামাজনের কোনো সার্ভিসগুলো কতটা অ্যাভাইলেবিলিটি দিচ্ছে তার জন্য এই কনসেপ্ট আমাদের লাগবে। যদিও এই ফাংশনালিটি MVP এর জন্য মেইন কনসার্ন নয়। আমরা জাস্ট কিছু ইউজারদের জন্য এই প্রোডাক্ট বানাবো। এরপর অ্যাপ্লিকেশনের ইউজের উপর ডিপেন্ড করে আমরা আমাদের অ্যাপ্লিকেশনের availablity বাড়াবো। + +> - Scalability (not main concern for MVP) + +এটাও MVP এর জন্য মেইন কনসার্ন না। ধরুন আমাদের অ্যাপ্লিকেশনের ইউজার দিন দিন বাড়তে থাকলো। শুরুতে ১০ জন। তারপরের মাসে ১০০ জন, এরপর ১০০০, ১০০০০ ইত্যাদি। এর সাথে সাথে আমাদের অ্যাপ্লিকেশনকেও scalable করতে হবে এত বিপুল পরিমাণ ইউজারদের জন্য। সার্ভার একাধিক ইউজ করতে হতে পারে। এটা যদিও MVP এর জন্য লাগে না, কিন্তু অ্যাপ্লিকেশন বানাতে গেলে এই জিনিসটা মাথায় রাখা প্রয়োজন। + +মূলত এগুলোই আমাদের নন ফাংশনালিটিজ রিকোয়ারমেন্টস। + +## SDLC Models + +প্রজেক্ট ম্যানেজমেন্টের জন্য আমরা সাধারণত দুইটা মডেল নিয়ে কাজ করি। Water Fall Model এবং Agile Model. এই দুইটা ছাড়াও আরো অসংখ্য মডেল আছে। আপনি SDLC (Software Development Life Cycle) model লিখে সার্চ দিলে আরো অসংখ্য মডেল পাবেন। কিন্তু এই দুইটি মডেল বছরের পর বছর ধরে সফলতার সাথে ব্যবহার হয়ে আসছে। +এখন প্রশ্ন হলো কোন মডেল আমরা ব্যবহার করবো। তার জন্য আমাদের এই দুইটি মডেল সম্পর্কে একটু ধারণা থাকা দরকার। + +### Water Fall Model + +ধরুন আমাদের রিকোয়ামেন্টস ফিক্সড। আগামী ৪/৫ বছরেও রিকোয়ারমেন্ট চেইঞ্জ হবে না। আমরা এই রিকোয়ারমেন্ট অনুসারে প্রোডাক্ট বানাবো। এরপর ৪/৫ বছর পর যদি প্রয়োজন হয় আপডেট করবো। এই ধরণের প্রজেক্টের ক্ষেত্রে ওয়াটার ফল মডেল খুবই কার্যকরী। +Water Fall Model + +উপরের ছবি থেকে আমরা বিষয়টা বুঝতে পারবো। আমরা এতক্ষণ পর্যন্ত যা করলাম তা সব Requirements এর অন্তর্গত। এখন এই রিকোয়ারমেন্টস অ্যানালাইসিস করে কোন আর্কিটেকচার ইউজ করতে হবে, কিভাবে আর্কিটেক্ট করতে হবে, কোন সিস্টেম ডিজাইন ইউজ করতে হবে, কি কি চ্যালেঞ্জ আসতে পারে, কি কি constrains আসতে পারে, কিভাবে আমরা এই প্রব্লেমগুলোকে সলভ করতে পারি, কিভাবে আমরা টাইম কমিয়ে আনতে পারি এই সবগুলো Design এর আলোচ্য বিষয়। এরপর আমরা UI/UX করি, ফ্রন্টএন্ড ডেভেলপমেন্ট করি, ব্যাকএন্ড ডেভেলপমেন্ট করি, এপিআই বানাই সবকিছু Implementation এর অন্তর্গত। এরপর আমাদের বিভিন্ন টেস্টিং এর মধ্য দিয়ে যেতে হবে। লোড টেস্ট, একসেপ্টেন্স টেস্ট, সিকিউরিটি টেস্ট ইত্যাদি টেস্ট পাশ করলে এরপর আমাদের অ্যাপ্লিকেশন আমরা ডেপ্লয় করতে পারি। এসমস্ত টপিক Verification এর অন্তর্গত। অ্যাপ্লিকেশন বানালে অবশ্যই বাগ থাকবেই। এই বাগ ডিবাগ করা Maintanence এর অন্তর্ভুক্ত। +এই মডেলের একটা বিরাট প্রব্লেম আছে। প্রব্লেমটা হলো ধরেন রিকোয়ারমেন্টস অ্যানালাইসিস শেষ, সেই অনুযায়ী ডিজাইনও শেষ। ইমপ্লিমেন্টেশনের ৫০% শেষ। এখন ক্লায়েন্ট এসে এমন একটা চেইঞ্জের কথা বললো যে পুরো সিস্টেমই ব্রেক হয়ে গেলো। এমন হলে আবার প্রথম থেকে সবকিছু শুরু করতে হবে। আপনার এতদিনের কষ্ট সব মাঠে মারা গেলো। + +### Agile Model + +উপরের মডেলে যে সমস্যার কথা আমরা বলেছিলাম সে সমস্যা থেকে পরিত্রাণের উপায় হলো Agile Model। এখন প্রশ্ন হলো What is Agile Model. Agile model হলো এক ধরণের Water Fall Model. ওয়াটার ফল মডেলে আমরা যে কাজটা করি তা একটা লং টার্মের জন্য। ২/৩ বছরের জন্য। অপরদিকে Agile model হলো ১৫ দিন বা ৭ দিনের জন্য একটা ওয়াটার ফল মডেল। ৭ দিনের জন্য একটা রিকোয়ারমেন্ট থাকবে। সেই অনুযায়ী ডিজাইন, ইমপ্লিমেন্টেশন, ভেরিফিকেশন হবে। পরবর্তী ৭ দিন অন্য রিকোয়ারমেন্ট নিয়ে কাজ হবে। এভাবে চক্রটা চলতে থাকবে। যেমন আমাদের অ্যাপ্লিকেশনে ধরেন আমরা প্রথম ১৫ দিন লোকাল অথেনটিকেশন নিয়ে কাজ করবো। পরবর্তী ১৫ দিন রোল বেইজড এক্সেস কন্ট্রোল নিয়ে কাজ করবো। এভাবে চলতে থাকবে। এখন দুই মাস পর যদি ক্লায়েন্ট এসে বলে অথেনটিকেশনে ইমেইল দিয়ে করা হইসে। এখন সে ফোন নাম্বারও অ্যাড করতে চাইছে। যেহেতু আমরা আলাদাভাবেই অথেনটিকেশন নিয়ে কাজ করেছিলাম সুতরাং শুধু সেটাই চেইঞ্জ করলে হবে। বাকি কাজের উপর কোনো সমস্যা আসবে না। জাস্ট ওয়াটার ফল মডেলই একটা নির্দিষ্ট শর্ট টাইম পর পর। +Agile Model +যেহেতু আমাদের অ্যাপ্লিকেশনটি MVP সুতরাং চোখ বন্ধ করে আমরা Agile Model সিলেক্ট করবো। + +## Choose Necessary Technology + +আমাদের অ্যাপ্লিকেশন ডেভেলপমেন্টের জন্য আমাদের বুঝতে হবে কোন টেকনোলজি আমাদের এই অ্যাপ্লিকেশন ডেভেলপমেন্টের জন্য উপযুক্ত। তার জন্য আমাদের একটা ল্যান্ডস্কেপ দরকার। এখানে একটা Overview দেয়া হলো যাতে একটা আইডিয়া আপনারা পেতে পারেন। + +প্রথমেই আমাদের দরকার SRS তৈরি করা। তার জন্য আমাদের যে যে টুলস লাগতে পারে তা হলোঃ + +- Google Docs, Microsoft Office, Notion. +- (To draw EML Diagram, ER Diagram, Activity Diagram etc.) draw.io, Lucid chart + +এরপর আমরা জাম্প করবো ডিজাইন পার্টে। এটাকে বলা হয় যেকোনো অ্যাপ্লিকেশনের Blue Print. এই পার্টে যা থাকবে তা হলোঃ + +- System Design + + - Architectural Decision + + - Monolithic (Layered) + - Easy to develop, but very complex to maintain when application grows + - Microservice + - Very hard to develop, but easy to maintain. + - Serverless (FAAS - Function as a service) + + Monolithic Application হলো একটা অ্যাপ্লিকেশনের সব ফিচার্স এক জায়গাতেই থাকবে। অপরদিকে Microservice হলো ঐ অ্যাপ্লিকেশনের সব ফিচারকে আলাদা করে ফেলে আলাদা আলাদা সার্ভারে রাখা। যেহেতু আলাদা আলাদা ডাটাবেইজ থাকবে সেহেতু তাদের মধ্যে কমিউনিকেশন সিস্টেম তৈরি করা একটু কঠিন। তাই সিঙ্গেল পার্সন বা ছোট টীমের জন্য Microservice avoid করাই ভাল। + যদি MVP হয় তাহলে চোখ বন্ধ করে Monolithic Architecture এ যেতে হবে। আর যেখানে বলা হবে আমার high availability and high scalability দরকার সেখানে আমরা মাইক্রোসার্ভিস ব্যবহার করবো। + + Serverless Architecture আমরা ইউজ করবো তখন, যখন ক্লায়েন্ট বলবে আমি MVP চাইছি, কিন্তু খুব সম্ভাবনা আছে এই অ্যাপ্লিকেশন খুব তাড়াতাড়ি গ্রো করার। যদি গ্রো করে ফেলে খুব তাড়াতাড়ি আমি চাই না কোনো ধরণের কোনো সমস্যা ইউজার ফেইস করুক বা ইউজার কোনো অভিযোগ আমাদের জানাক। কারণ এটা হাইলি স্কেলেবল। + + - Select Database + অনেক রকমের ডাটাবেইজ হয়েছে মার্কেটে। কোন কাজের জন্য কোন ধরণের ডাটাবেইজ লাগবে তা আমাদের খুঁজে বের করতে হবে। + - SQL (যখন আমি জানি যে ডাটাতে কি কি প্রোপার্টি থাকবে না থাকবে) + - PostgreSQL + - MySQL + - NoSQL (যখন রিকোয়ারমেন্ট চেইঞ্জ হবে র‍্যানডমলি) + - MongoDB + - Casandra + - Key value + - Redis (ক্যাশিং, জব শিডিউলিং, ম্যাসেজ ব্রোকার) + - DynamoDB + - Graph Database (গ্রাফ স্ট্রাকচারের ডাটাগুলোর জন্য) + - Neo4j + - Search Database (সার্চ ইঞ্জিন রিলেটেড কাজ করে) + - Elastic Database (Open Source - Text Search) + - Algolia Search (Open AI 3 - Natural Language Processing) যেমন আমি সার্চ করলাম Men's favorite pet তখন সে আমার লেখা পড়ে মানুষের প্রিয় পোষা প্রাণী কি হতে পারে সেটা সার্চ করে দিবে। এটা অনেক পাওয়ারফুল কিন্তু ফ্রি নয়। + +- Software Architecture + Software Architecture ডিরেক্টলি রিলেটেড টু কোডিং। এটা পুরোপুরি ডিপেন্ড করে সিস্টেম ডিজাইনের উপর। সফটওয়্যার আর্কিটেকচারের জন্য আমাদের যা যা জানতে হবে তা হলোঃ + - OOP + - OOP Design Pattern & Principles + - DSA + - Clean Code Architecture + - Problem Solving +- Testing + - Test Driven Development (আগে টেস্ট কোড লিখে এরপর মেইন কোড লিখবো) + - E2E (End to End) (হেডলেস ব্রাউজার ইউজ করে টেস্ট করা) +- Distribution + - DevOps (যে কাজগুলো অপারেশনাল রিলেটেড সেই কাজগুলো অটোমেট করা যায় এর মাধ্যমে) + - Continuous - যে কথাগুলোর সাথে কন্টিনিউ কথাটা আছে সেখানেই ডেভঅপ্স। নিচের ছবি দেখলে আরো ক্লিয়ার হবে। + devops + - Git and Github + - Linux, SSH - Secured Shell Homepage + - Circle CI, Travis CI, Jenkins (Integration server) + - Docker (For Single Service), Docker Compose (For Multiple Service) + - Docker Swarm, Kubernetes (Multiple Place এ থাকা অ্যাপ্লিকেশন এক জায়গা থেকে ম্যানেজ করার জন্য) + - Prometheus, Nagios (Continuous monitoring এর জন্য) + - Ansible + - Cloud Engineering - আমাদের একটা ক্লাউড দরকার যেখানে আমরা আমাদের অ্যাপ্লিকেশনকে ডেপ্লয় করতে পারি। এখানে অসংখ্য ডিজাইন ডিসিশন চলে আসে। যেমন + - VPC (Virtual Private Cloud) এর মাধ্যমে এই ক্লাউডের মধ্যে যে অ্যাপ্লিকেশন আছে তারাই শুধুমাত্র আমার ডাটাবেইজের এক্সেস নিতে পারবে এই ব্যবস্থা করে দেয়া যাবে। + - EC2 (Elastic Computing Cloud) Actual server যে আমার অ্যাপ্লিকেশনকে রান করবে + - S3 (Provide Storage Service) + - RDS (Relational Database Service) + - DynamoDB + - Route 53 + - CloudFront (CDN) + - SES (Simple Email Service) + - SNS (Simple Notification Service) + - SQS (Simple Queue Service) + - API Gateway + - Lambda + - Step Functions + - Cloud Watch + - Cloud Formation, Terraform, CDK + এগুলো ব্যবহার করার মাধ্যমে আমরা Non functionalities requirements প্রোভাইড করতে পারবো। +- Maintenance + +## Decision + +উপরে যা শিখলাম তার উপর ভিত্তি করে এবার আমাদের অ্যাপ্লিকেশনের ডিসিশন নিয়ে নিই। যেহেতু MVP হবে এবং বলা আছে কোনো হাই এভেইলেবিলিটি বা স্কেলেবিলিটির দরকার নেই তাই আমরা ক্লাউড ইঞ্জিনিয়ারিং পার্ট বাদ দিতে পারি। জাস্ট ৪/৮ জিবি র‍্যামের সার্ভার হলেই চলবে। ইনিশিয়ালি ডেভঅপ্সেরও প্রয়োজন নাই কারণ আমাদের দ্রুত একটা ওয়ার্কেবল প্রোটোটাইপ লাগবে। পরে অবশ্যই দরকার হবে। + +- যেহেতু MVP, সেহেতু Monolithic Architecture use করবো। +- যেহেতু ডিসিশন র‍্যানডম চেইঞ্জ হবে তাই আমরা ইউজ করবো NoSQL ডাটাবেইজ (MongoDB Atlas). +- ব্যাকএন্ডের জন্য আমাদের দুইটা প্রশ্ন করতে হবে নিজেদের। প্রথম প্রশ্ন আমাদের অ্যাপ্লিকেশনে হেভি কম্পিউটিং করতে হবে কিনা (ভিডিও প্রসেসিং / ইমেজ প্রসেসিং)? যদি না হয় তাহলে .Net, Java Spring boot, Go lang বাদ। দ্বিতীয় প্রশ্ন হলো আমার অ্যাপ্লিকেশন MVC, multipage কিনা? যদি হয় তাহলে NodeJS বাদ। আমরা সিলেক্ট করবো Django, Laravel বা Ruby on rails. যদি MVC না হয়, ডেটা ড্রাইভেন হয় এবং রিয়েল টাইম হয় তাহলে চোখ বন্ধ করে NodeJS. আমাদের অ্যাপ্লিকেশন ডেটা ড্রাইভেন তাই আমাদের অ্যাপ্লিকেশনের জন্য সিলেক্ট করবো NodeJS. কারণ সে কোনো রিকোয়েস্ট ব্লক করবে না। এক্ষেত্রে নোড জেএসের ফ্রেমওয়ার্ক হিসেবে Hapi, Express বা Fastify ব্যবহার করতে পারি। + +## Resource for this lecture + +এই লেকচারের সমস্ত রিসোর্স এই [লিংক](../../resources/lecture-01/README.md) এ পাবেন। + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/full-stack-army/articles/application-requirements-and-landscape/Waterfall_model.png b/full-stack-army/articles/application-requirements-and-landscape/Waterfall_model.png new file mode 100644 index 0000000..355b2eb Binary files /dev/null and b/full-stack-army/articles/application-requirements-and-landscape/Waterfall_model.png differ diff --git a/full-stack-army/articles/application-requirements-and-landscape/agile-model-of-se.png b/full-stack-army/articles/application-requirements-and-landscape/agile-model-of-se.png new file mode 100644 index 0000000..c1c8c65 Binary files /dev/null and b/full-stack-army/articles/application-requirements-and-landscape/agile-model-of-se.png differ diff --git a/full-stack-army/articles/application-requirements-and-landscape/devops.png b/full-stack-army/articles/application-requirements-and-landscape/devops.png new file mode 100644 index 0000000..2ae92b0 Binary files /dev/null and b/full-stack-army/articles/application-requirements-and-landscape/devops.png differ diff --git a/full-stack-army/articles/understand-programming-languages/README.md b/full-stack-army/articles/understand-programming-languages/README.md new file mode 100644 index 0000000..0653806 --- /dev/null +++ b/full-stack-army/articles/understand-programming-languages/README.md @@ -0,0 +1,66 @@ +# Understand Programming Languages + +## Difference between programming & programming language + +**Programming:** programming is a common keyword nowadays we are hearing a lot. Before explaining what programming is, we must know first that what actually program is. In computing, a program is a specific set of ordered operations for a computer to execute tasks and user command. the program is put into a storage area accessible to the computer. + +Now let's come to the point to understand the programming. Programming is the process of creating and developing an executable machine program which performs a set of instructions. If we elaborate this line, it stands - Programming deals with the much bigger picture, which involves in all the critical parameters from planning, designing, coding, debugging and compiling to testing and implementation. It handles the core functionality between human inputs and +proper machine level outputs. + +**programming language:** We are going to talk about programming languages. Just few moment back we’ve learned what is program and what is programming. Do you know what is language? Language is nothing but a medium of communication. When we talk with a person, we use languages to make him or her to understand what we are telling about. And also we do one more thing. If we communicate with a Bengali person we speak in Bangla. Or if we communicate with it English person, we speak in English. In the same way, when we communicate with the computer, we communicate by their own languages, which is called programming languages. In short, a programming language is a vocabulary and set of grammatical rules for instructing a computer or computing device to perform specific tasks. There are several programming languages, i.e., small talk, pearl, Lua, C, C++, Java, Ruby, PHP, Python, Javascript, scala etc. And each programming language has a unique set of keywords (words that it understands) and a special syntax for organizing program instructions. So, when we want a machine to perform a task, we instruct it about the task by programming language. + +## Explain programming paradigms with examples + +We know about programming already. But what is the meaning of paradigm? Paradigm is a school of thought or model that has distinct features, frameworks, patterns, and style which help you solve a particular problem. In a simple word, the term programming paradigm refers to a style of programming. It does not refer to a specific language, but rather it refers to the way you program. This style or way is similar to blueprint. A blueprint of doing something. + +There are lots of programming languages that are well-known but all of them need to follow some strategy when they are implemented. And that strategy is a paradigm. + +The types of programming paradigms: + +1. **Imperative Programming Paradigm:** The imperative paradigm is said to be command driven. The program code in imperative paradigm programming languages directs the program execution as sequence of statements executed one by one. In imperative style program, the programmer has to elaborate each statement in detail. Each statement directs what is to be done and how it is to be done. + 1. **Procedural:** It is a programming paradigm based upon the concept of procedure calls, in which statements are structured into procedures (also known as subroutines or functions). They are a list of instructions to tell the computer what to do step by step, Procedural programming languages are known as top-down languages. Most of the early programming languages are all procedural. + 2. **object-oriented:** In this framework, all real-world entities are represented by Classes. Objects are instances of classes so each object encapsulates a state and behavior. State implies the fields, attributes of the object and behavior is what you do with the state of the object and they are the methods. Objects interact with each other by passing messages. +2. **Declarative Programming Paradigm:** The declarative paradigm is a programming paradigm that is focused on the logic of the program and the end result. In this paradigm that control flow is not the important element of the program. The main focus of the declarative style of programming is achieving the end result. This paradigm is straight forward and to the point while writing the program code. + 1. **Functional:** Functional programming is a programming paradigm where you have a style of building the structure and elements of computer programs. Here you treat computation as an evaluation of mathematical functions, and you avoid changing state and mutable data. + 2. **Logical:** The Logical Paradigm takes a declarative approach to problem-solving. Various logical assertions about a situation are made, establishing all known facts. Then queries are made. The role of the computer becomes maintaining data and logical deduction. + +## How to choose the perfect programming language to create an application? + +To choosing a programming language for an application, we need to do some homework first. which is, understand the types of the application, purpose of it and its requirement analysis. It will give us a list that can serve as a starting point to choose the programming language that we’ll need. Then we can see the options for choosing otherwise we cannot. If we go deeper down of the topic it will be easy to understand what I am telling. When the question arises to choose a programming language, we can ask the following questions to find out the best fit programing language for an application: + +- What type of application is going to be made? +- What is life time of the application will be? +- What’s the environment that the project will run on — web, mobile, desktop etc.? +- What is the complexity of the application? +- What are the deployment considerations? Are there infrastructure considerations to using the programming language? +- Does the client prefer a certain programming language to be used? +- Which language have proper ecosystem support? Will the language survive for the long haul? +- Are there specific libraries, features, and tools for a programming language that are the + industry standard for this type of project? +- Which language is more preferable for this application in terms of Maintainability? +- What are the constraints of the project that are non-negotiable — time, budget, + resources? +- What are the performance considerations of the project? which language accommodate the benchmarks and the performance? +- Does it need to integrate with third-party tools? +- Are there any security considerations? +- Are there any Scalability and performance considerations? + +Here are some practices that programming languages being used today on the basis of types of application. i.e., + +- **Web Applications:** JavaScript, PHP, Ruby, TypeScript +- **Mobile Applications:** Swift, Java, JavaScript, Object-C +- **Operating Systems:** C, C++ +- **Distributed Systems:** Go +- **Enterprise Applications:** Java, C#, C++, ErLang +- **Analytics & Machine Learning:** Python, R, Clojure, Julia +- **Math & Scientific Computing:** Matlab, FORTRAN, ALGOL, APL, Julia, R, C++ +- **Data Visualization:** Python, R, Java, C# +- **Big Data:** Java, Python, R, Scala, Clojure +- **Data Storage:** SQL, C#, Java, Python + +## Explain programming is always the same but the language varies + +If we have clear understanding on programming and programming languages we already have the answer. Programming is always same, because, programming is the process of creating and developing an executable machine program which performs a set of instructions. So, whatever language we use to code to solve that set of instruction, the process is declared and predefined. + +--- +**Author:** Jahedul Huda Chowdhury diff --git a/full-stack-army/articles/we-need-freedom/README.md b/full-stack-army/articles/we-need-freedom/README.md new file mode 100644 index 0000000..301574e --- /dev/null +++ b/full-stack-army/articles/we-need-freedom/README.md @@ -0,0 +1,160 @@ +# We Need Freedom, We have to Stop Technology War + +লাস্ট ক্লাসে আমরা যে সমস্ত টেকনোলজির নাম দেখেছিলাম তাতে অনেকেরই ভয় পাওয়া স্বাভাবিক যে এত এত জিনিস কিভাবে আমি শিখবো। আপনাদের ভয় পাওয়ার কোনো প্রয়োজন নেই। টেকনোলজি শেখার খুব ভাল উপায় হচ্ছে কোনোকিছুতে মাস্টার হওয়া যাবে না একবারেই। ধীরে ধীরে হতে হবে। ধরেন আপনি এইচটিএমএল দিয়ে শুরু করলেন। এখন আপনি এইচটিএমএলের একদম সমস্ত বিষয় জেনে তারপর সিএসএসে যেতে চান তাহলে আপনার আর যাওয়া হবে না। উপায় কী? উপায় হলো এইচটিএমএলের একটা বেসিক আন্ডারস্ট্যান্ডিং নেয়া। যেমন কিভাবে এইচটিএমএল স্ট্রাকচার লিখতে হয়, কিভাবে লে-আউট করতে হয়, কিভাবে টেক্সট, ইমেজ অ্যাড করতে হয় মানে জাস্ট বেসিকটা নিয়ে সিএসএসে জাম্প করা। আপনি সিএসএস শিখতে গেলে আল্টিমেটলি আপনার এইচটিএমএল লাগবেই। তখন যখন আপনার মনে হবে এইচটিএমএলের এই জিনিসটাতো আমি জানিনা তখন আপনি পিছনে এসে ওটা দেখে আবার সামনে যাবেন। এবার সিএসএসের যে সব শিখতে হবে তা নয়। দৈনন্দিন আপনার যা লাগবে তাই শিখে জাভাস্ক্রিপ্টে জাম্প করেন। কারণ জাভাস্ক্রিপ্ট শিখতে গেলে আপনাকে এইচটিএমএল সিএসএস লিখতেই হবে। সুতরাং কিছু বাদ গেলে তা শেখা যাবেই। চিন্তার কিছু নেই। + +আপনি কোনো জিনিস শিখতে গেলে যখন কোনো একটা টপিক বুঝবেন না তখন সেটা স্কিপ করে যান। একসময় গিয়ে দেখবেন যেটা বুঝেননি সেটা বুঝার জন্য যে ধরণের ব্রেইনস্টর্মিং দরকার তা হয়ে যাবে। এরপর একটু ব্যাকে এসে তা শিখে নিন। এভাবে হলে কোনো জিনিস শিখতে আপনার বেশি সময় লাগবে না। এবং ভয়ও লাগবে না। + +## আজকের এজেন্ডা + +এবার আমাদের আজকের এজেন্ডায় আসা যাক। আজকের এজেন্ডাগুলো হলোঃ + +- We need freedom, we need to stop technology war +- Why do we need programming language? +- Why different programming languages for client & server? +- Understanding programming paradigms. +- Programming is always same, but the language varies. + +## Why do we need programming language? + +একটা অ্যাপ্লিকেশনের জন্য যে যে স্টেপ লাগবে তা হলোঃ + +- Requirements +- Design +- Implementation + - UI/UX Design + - Web Design + - Frontend Development + - Backend Development + - Test Code +- Testing, Deployment +- Maintenance + +উপরোক্ত স্টেপগুলোর মধ্যে থেকে আমাদের Implementation স্টেপে মূলত প্রোগ্রামিং ল্যাঙ্গুয়েজ লাগছে। আরো স্পেসিফিকভাবে বলতে গেলে ফ্রন্টএন্ড এবং ব্যাকএন্ড ডেভেলপমেন্টে প্রোগ্রামিং ল্যাঙ্গুয়েজ লাগছে। এখন প্রশ্ন হলো কেন লাগছে। প্রোগ্রামিং ল্যাঙ্গুয়েজ দিয়ে আমরা কী করি? সহজ ভাষায় বলতে গেলে প্রোগ্রামিং ল্যাঙ্গুয়েজ একটা কাজই করে। সেটা হলো আমরা প্রোগ্রামিং ল্যাঙ্গুয়েজ দিয়ে কিছু কোড লিখি যা দিয়ে আমাদের কম্পিউটার বুঝতে পারে আমরা কি বুঝাতে চাইছি। প্রোগ্রামিং ল্যাঙ্গুয়েজের মাধ্যমে আমরা কোনো রিয়েল লাইফ প্রব্লেম কম্পিউটারকে বুঝাতে পারি। এই কাজ বাদে দুনিয়ার কোনো প্রোগ্রামিং ল্যাঙ্গুয়েজের আর কোনো কাজ নেই। যদিও প্রতিটা ল্যাঙ্গুয়েজ আসার পিছনে বিভিন্ন কারণ আছে। কিন্তু কাজ ঘুরেফিরে একটাই। প্রতিটা ল্যাঙ্গুয়েজ কোনো না কোনোভাবে আমার আর কম্পিউটারের মাঝে একটা ট্রান্সলেটর হিসেবে কাজ করছে। ধরুন, একজন আমেরিকান মানুষ, তার সাথে কমিউনিকেট করতে হবে। এখন চাইনিজরা চাইনিজ ট্রান্সলেটর আনবে, জার্মানরা জার্মান ট্রান্সলেটর, স্প্যানিশরা স্প্যানিশ ট্রান্সলেটর। কিন্তু উদ্দেশ্য একটাই। কম্পিউটাররূপী আমেরিকানের সাথে কমিউনিকেট করা। স্বাভাবিকভাবেই জার্মান, চাইনিজ বা স্প্যানিশদের ইংলিশ একসেন্ট তো আর আমেরিকান একসেন্টের মতো হবে না। ভাষাগত একটু পার্থক্য থাকবে। কিন্তু উদ্দেশ্য যেটা যে আমেরিকানের সাথে কমিউনিকেট করা সেটা সফল। + +## Why different programming languages for client & server? + +একটা বড় প্রশ্ন আছে। যদি সব ল্যাঙ্গুয়েজই কম্পিউটারের সাথে কমিউনিকেট করার জন্য হয় তাহলে এত ল্যাঙ্গুয়েজ কেন? এর উত্তর বিভিন্নভাবে দেয়া যায়। ধরেন আমরা বাংলাদেশী। এখন আমরা যখন ইংরেজিতে কিছু বুঝাতে চাইবো তখন আমরা আমাদের মধ্য থেকে একজনকে ট্রান্সলেটর হিসেবে নিয়োগ দিবো। এরকম ভারতীয়রা ভারতীয়দের, জার্মানরা জার্মানদের, চাইনিজরা চাইনিজদের নিয়োগ দিবে। প্রতিটা দেশ তাদের মতো করে তাদের প্রব্লেম সলভ করার জন্য তাদের মতো করে ট্রান্সলেটর নিয়োগ দিবে। সেরকম যখন মাইক্রোসফট একটা প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি করেছে তখন তারা তাদের প্রব্লেম সলভ করার জন্য প্রোগ্রামিং ল্যাঙ্গুয়েজটা তৈরি করেছে। যখন অ্যাপল একটা প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি করেছে তখন তারা তাদের প্রোডাক্টের সাথে খাপ খায় এমন একটা প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি করেছে। যখন ডেনিস রিচি ইউনিক্স অপারেটিং সিস্টেম তৈরি করতে গিয়ে আটকে গেলেন তখন তিনি ঐ সময়কার প্রব্লেম সলভ করার জন্য প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি করেছেন। এরপর সি যেসব প্রব্লেম সলভ করতে পারছিল না সেসব সলভ করার জন্য অবজেক্ট অরিয়েন্টেড ভিত্তিক ল্যাংগুয়েজ সি++ আসলো। সান মাইক্রোসিস্টেম যখন আসলো তখন তারা তাদের প্রব্লেম সলভ করার জন্য জাভা প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি করলো। তাহলে দেখা যাচ্ছে প্রতিটা কোম্পানি তাদের নিজস্ব প্রব্লেম সলভ করার জন্য বিভিন্ন প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি করেছে। + +আজকে জাভাস্ক্রিপ্ট নিয়ে এত তোলপাড়। এই জাভাস্ক্রিপ্ট মেইনলি আসার পিছনে কারণ ছিল ব্রাউজারের এইচটিএমএলে কিছু interactivity যোগ করা। এটাই ছিল আসল কারণ। জাভা যখন এসেছিলো তখন উদ্দেশ্য ছিল পোর্টেবল ডিভাইসে (এমবেডেড ডিভাইস, মিডিয়া প্লেয়ার, সিডি প্লেয়ার) প্রোগ্রাম রান করা। সি আসার উদ্দেশ্য অপারেটিং সিস্টেম ডিজাইন করা। সুতরাং দেখা যাচ্ছে একেকটা প্রোগ্রামিং ল্যাঙ্গুয়েজ একেকটা কারণে বাজারে এসেছে। তাই তাদের ফ্লেভারও ভিন্ন ভিন্ন। কিন্তু উদ্দেশ্য একটাই, প্রব্লেম সলভ করা। আমাদের মানুষের সাথে কম্পিউটারের কমিউনিকেশন তৈরি করা। + +পিএইচপি আসার একটা সুন্দর ইতিহাস আছে। যখন ওয়েব আসলো তখন ওয়েবসাইট সুন্দর করে তৈরি করার জন্য একটা স্ক্রিপ্টিং ল্যাঙ্গুয়েজের প্রয়োজন ছিল। যেটা এক্সিস্টিং কোনো ল্যাঙ্গুয়েজ সলভ করতে পারছিলো না। তখন ছিল ওয়েব ১.০। শুধু ডাটা পাস হবে। সেই উদ্দেশ্যে ক্রিয়েটর পিএইচপি তৈরি করেন। পরবর্তীতে তা যথেষ্ট জনপ্রিয়তা লাভ করে। + +প্রত্যেকটা ল্যাঙ্গুয়েজের একটা নির্দিষ্ট উদ্দেশ্য আছে। কে তৈরি করছে তার প্রয়োজন অনুসারে সামান্য পরিবর্তন হচ্ছে। কিন্তু ঐ যে বললাম উদ্দেশ্য সবার একই। প্রব্লেম সলভ করা। + +এখন আমাদের মেইন প্রশ্নে আসি। ডিফারেন্ট ল্যাঙ্গুয়েজ কে প্রয়োজন ক্লায়েন্ট এবং সার্ভারের জন্য? + +প্রথম প্রশ্ন হলো প্রোগ্রামিং ল্যাঙ্গুয়েজ রান করে কোথায়? অবশ্যই একটা মেশিনে। কম্পাইলারে কোড কম্পাইল হওয়ার পর আল্টিমেটলি মেশিনে রান হয়। সেটা হতে পারে কম্পিউটার, সেটা হতে পারে টিভি, সেটা হতে পারে ফ্রিজ। মোটকথা যেখানে প্রসেসর আছে, কিছু র‍্যাম আছে, কিছু মেমোরি স্টোরেজ আছে যেখানে আমার কোডটা রাখা যাবে আর একটা কম্পাইলার ইনস্টল করা যাবে সেখানেই প্রোগ্রাম রান করানো যাবে। + +যদি তাই হবে তাহলে আমার ব্রাউজার বা ক্লায়েন্টের জন্য এক ধরণের ল্যাঙ্গুয়েজ আর সার্ভারের জন্য অন্যধরণের ল্যাঙ্গুয়েজ কেন? এই ওয়েবের জগতে ওয়েবসাইটের ফ্রন্টএন্ডের যে সেকশনটা আছে সেটার প্রোগ্রাম রান হবে একটা ব্রাউজারে। এখন ব্রাউজার তো কম্পিউটার না। ব্রাউজার হলো কম্পিউটারের মধ্যে থাকা ছোট একটা সফটওয়্যার। আপনি কম্পিউটারে চাইলে সি, সি++, জাভা, পাইথন ইত্যাদি যেকোনো কম্পাইলার ইনস্টল করতে পারবেন। কিন্তু ব্রাউজারে তো এতো কিছু সম্ভব না। সেজন্য ব্রাউজার বানানোর সময় জাভাস্ক্রিপ্ট নামক একটা ল্যাঙ্গুয়েজ সেখানে দিয়ে দেয়া হয়েছে। সুতরাং আমাদের জাভাস্ক্রিপ্ট শিখতে হবে ব্রাউজারের ইন্টের‍্যাক্টিভ ফিচারগুলো অ্যাড করার জন্য। আর কোনো ল্যাঙ্গুয়েজ এখানে কাজ করবে না। সুতরাং আমাকে ওয়েব অ্যাপ ডেভেলপ করতে হলে অবশ্যই জাভাস্ক্রিপ্ট শিখতেই হবে। যদি কাল এমন কোনো ব্রাউজার বাজারে চলে আসে যে তার কাজ হবে পাইথনে তাহলে আমাদের ফ্রন্টএন্ডের কাজ করার জন্য অবশ্যই পাইথন শিখতে হবে। কিছু করার নাই। কিছু মানুষ সিদ্ধান্ত নিয়েই নিয়েছে ব্রাউজারে জাভাস্ক্রিপ্ট ছাড়া আর কোনো ল্যাঙ্গুয়েজ তারা সহ্য করবে না। তাই আমাদের জাভাস্ক্রিপ্ট শেখার বিকল্প নেই ফ্রন্টএন্ডের জন্য। +এবার আসি সার্ভারের ব্যাপারে। এটাই হলো আল্টিমেট মেশিন। আমরা যে রিকোয়েস্টগুলো পাঠাই এটি তা সার্ভ করে, তাই এর নাম হচ্ছে সার্ভার। আমরা যদি বলি এই পেইজটা দাও বা এই ইমেজটা দাও বা এই পোস্ট বা ভিডিওটা দাও, সে সার্ভ করে দিলো। বেসিক্যালি এটা একটা কম্পিউটার। কম্পিউটারে যেকোনো প্রোগ্রামিং ল্যাঙ্গুয়েজ রান করতে পারে। + +তাহলে দেখা যাচ্ছে ব্রাউজার বা ক্লায়েন্টের জন্য আমরা জাভাস্ক্রিপ্টের বাইরে যেতে পারি না। কিন্তু যেহেতু সার্ভার একটি কম্পিউটার আর কম্পিউটারে যেকোনো ল্যাঙ্গুয়েজ রান করানো যায় সুতরাং আমরা সার্ভারের ল্যাঙ্গুয়েজ সিলেক্টের ব্যাপারে উন্মুক্ত। আমরা সি, সি++, জাভা, গো, পাইথন, এমনকি অ্যাসেম্বলি ল্যাঙ্গুয়েজ ব্যবহার করেও একটা সার্ভার সাইড অ্যাপ্লিকেশন বানিয়ে ফেলতে পারি। কিন্তু ডিফারেন্ট ল্যাঙ্গুয়েজের দিন ছিল ২০০৯ সাল পর্যন্ত। ২০০৯ সালের আগ পর্যন্ত জাভাস্ক্রিপ্টকে ব্রাউজারের বাইরে কেউ কল্পনাও করতে পারেনি। তাই একরকম বাধ্য হয়েই তখন ব্রাউজার বা ক্লায়েন্টের জন্য জাভাস্ক্রিপ্ট আর সার্ভারের জন্য পাইথন, পিএইচপি, রুবি ইত্যাদি ল্যাঙ্গুয়েজ শিখতে হতো। এজন্য আমাদের মাল্টিপল ল্যাঙ্গুয়েজের প্রয়োজন ছিল। +কিন্তু রায়ান ডেলকে ধন্যবাদ তিনি Nodejs ক্রিয়েট করার কারণে এখন আর ক্লায়েন্ট আর সার্ভারের জন্য মাল্টিপল ল্যাঙ্গুয়েজ শিখতে হয় না। এক জাভাস্ক্রিপ্ট শিখলেই ফ্রন্টএন্ডের কাজও করা যায়, আবার API বানানো যায়, ব্যাকএন্ড বানিয়ে ফেলা যায়। নোড জেএস একটা রেভ্যুলেশন। ডেভেলপমেন্ট জগতকে আরো সহজ করার পাশাপাশি জাভাস্ক্রিপ্টকে সার্ভার সাইড ল্যাঙ্গুয়েজ হিসেবে শক্ত অবস্থানে আজ পৌঁছে দেয়ার কৃতিত্ব নোড জেএস এবং অবশ্যই রায়ান ডেলের। + +তবে একটা ল্যাঙ্গুয়েজ দিয়ে দুইদিকেই কাজ করতে পারলেও সার্ভার সাইডের জন্য আমরা উন্মুক্ত। আমরা যেকোনো ল্যাঙ্গুয়েজ দিয়েই সার্ভার সাইড অ্যাপ্লিকেশন বানাতে পারি। + +এখন আমি ব্রাউজারের জায়গায় যদি মোবাইল নিয়ে আসি তাহলে কী হবে? মোবাইলের নরমালি আমরা দুইটা অপারেটিং সিস্টেমের সাথে পরিচিত। অ্যান্ড্রয়েড এবং আইওএস। আইওএস বানিয়েছে অ্যাপল। সে রেস্ট্রিক্টেড করে দিলো আমার অপারেটিং সিস্টেমে সুইফট (Swift) আর অবজেক্টিভ সি ছাড়া কোনো ল্যাঙ্গুয়েজ আমি ঢুকতে দিবো না। কিন্তু অ্যান্ড্রয়েডের কোনো ব্যারিয়ার নাই। সেখানে সি++, কটলিন, জাভা যেকোনো ল্যাঙ্গুয়েজ দিয়েই অ্যাপ বানানো যেতে পারে। শুধু এই তিন ধরণের ল্যাঙ্গুয়েজ দিয়েই যে অ্যান্ড্রয়েড অ্যাপ বানানো যায় তা নয়। আমরা যদি অপারেটিং সিস্টেম ভাংতে পারি, মানে অ্যান্ড্রয়েড ফোন রুট করতে পারি তাহলে পুরো সিস্টেমের এক্সেস মেশিনের কাছে চলে আসবে। আর এক্সেস আসার পরে যেহেতু মোবাইল একটা মেশিন, এর প্রসেসর, র‍্যাম, হার্ডডিস্ক সব আছে সুতরাং এখানে যেকোনো বাইনারি কোড রান করানো যাবে। এখন আমরা অ্যান্ড্রয়েডে লিনাক্স ইউজ করতে পারি, প্রোগ্রামিং করতে পারি কারণ এটা একটা মেশিন। এতে যেকোনো ল্যাঙ্গুয়েজ রান করানো যাবে। কিন্তু আইওএসের ক্ষেত্রে ওরা ব্যারিয়ার তৈরি করে রেখেছে, সেখানে কিছু করার নেই। + +**সারমর্মঃ** ক্লায়েন্টের জন্য শুধুই জাভাস্ক্রিপ্ট আর সার্ভারের জন্য জাভাস্ক্রিপ্টসহ যেকোনো ল্যাঙ্গুয়েজ। আর মোবাইলের ক্ষেত্রে আইওএসে সুইফট আর অবজেক্টিভ সি ছাড়া অন্য ল্যাঙ্গুয়েজ ব্যবহার করা যায় না, আর অ্যান্ড্রয়েডের ক্ষেত্রে কোনো ব্যারিয়ার নেই। + +## Understanding programming paradigms + +এই যে প্রোগ্রামিং এর যুদ্ধ তার মূলে হলো এই প্রোগ্রামিং প্যারাডাইম। এখন প্যারাডাইম মানে কী? বইয়ের ভাষায় বলতে গেলে অনেক কমপ্লেক্স হয়ে যাবে। সহজ ভাষায় বললে বলতে হয় এটা অনেকটা ক্যাটাগরির মতো। যেমন এই প্রোগ্রামিং ল্যাঙ্গুয়েজ এই ধরণের কাজ করবে, ঐ প্রোগ্রামিং ল্যাঙ্গুয়েজ ঐ ধরণের কাজ করবে। এভাবে সব ল্যাঙ্গুয়েজকে বিভিন্ন ক্যাটাগরির মধ্যে ফেলাকে প্রোগ্রামিং প্যারাডাইম বলে। + +আমদেরকে যখন প্রোগ্রামিং শেখানো হয় তখন প্রোগ্রামিং এর মতো করে শেখানো হয় না। হয় ল্যাঙ্গুয়েজ স্পেসিফিকভাবে। এখন অনেকদিন ধরে একটা ল্যাঙ্গুয়েজ শিখতে শিখতে তার প্রতি ভালবাসা থেকে বলেন বা অন্য ল্যাঙ্গুয়েজ শেখার প্রতি অনীহা থেকেই বলেন আমরা একটা ল্যাঙ্গুয়েজের উপর স্পেসিফিক হয়ে যাই। কিন্তু যদি abstractly আমাদের প্রোগ্রামিং শেখানো হতো তাহলে আর ল্যাঙ্গুয়েজ নিয়ে আমাদের ভাবতে হতো না। নিজেদের প্রয়োজনে যেকোনো ল্যাঙ্গুয়েজ আমরা সহজেই বুঝে নিয়ে ব্যবহার করতে পারতাম। এখন সেটার জন্য দরকার প্রোগ্রামিং ল্যাঙ্গুয়েজগুলো কি কি প্রব্লেম সলভ করে তার একটা শ্রেণীবিন্যাস করা। আর এক্ষেত্রে তা হলো প্রোগ্রামিং প্যারাডাইম। + +প্রোগ্রামিং এর মূলত দুইটা প্যারাডাইম আছে। যথাঃ ১. ইম্পেরেটিভ প্যারাডাইম ২. ডেক্লারেটিভ প্যারাডাইম +উইকিপিডিয়ার মতে ইম্পেরেটিভ প্যারাডাইম হলো, `in which the programmer instructs the machine how to change its state` এবং ডেক্লারেটিভ প্যারাডাইম হচ্ছে, `in which the programmer merely declares properties of the desired result, but not how to compute it` + +এখন একটু সহজ করে বুঝানো যাক। ধরেন আপনি কাউকে বললেন, ভাই আমি এই বইয়ের নামটা লিখে দিলাম। আপনি নীলক্ষেত থেকে কষ্ট করে বইটা কিনে আনেন। আমি শুধু বলেছি নীলক্ষেত থেকে বই কিনে আনতে। আমি বলিনি কিভাবে যাবে। সেটা যে যাবে তার উপর নির্ভর করবে। এটা হলো ডেক্লারেটিভ প্যারাডাইম। তাহলে এবার সংজ্ঞা থেকে বুঝুন যেখানে প্রোগ্রামার জাস্ট রেজাল্টটা আসার জন্য প্রোপার্টিজ ডিক্লেয়ার করবে, কিন্তু বলবে না কিভাবে সেটা করতে হবে। + +এবার আসা যাক ইম্পেরেটিভ প্যারাডাইমের কাছে। ধরেন আমি সেই বই কিনে আনতে বললাম কিন্তু প্রোপার ইনস্ট্রাক্ট দিয়ে। ইনস্ট্রাকশনগুলো নিচে দেয়া হলোঃ + +- ধরলাম আমার বাসা ফার্মগেইট। বললাম আপনি ফার্মগেটের গোল চত্ত্বরে যাবেন। +- সেখানে ১০ নম্বর বাস পাবেন। সেটাতে উঠে ১০ টাকা ভাড়া দিবেন। +- এরপর নীলক্ষেতের ঠিক মোড়ে নামবেন। +- মোড়ে নামার পরে বামদিকে তাকাবেন। +- তাকানোর পর একটা লাইব্রেরি পাবেন। +- লাইব্রেরিতে ঢুকে যাবেন। +- ঢুকার পর দেখবেন অমুক বুক হাউজ নামে একটা হাউজ আছে +- সেই হাউজে ঢুকে এই বইয়ের দাম জিজ্ঞেস করবেন। +- বইয়ের দাম যদি ২০০ টাকার মধ্যে হয় তাহলে কিনবেন। +- যদি বেশি হয় পাশের দোকান যাবেন। +- গিয়ে দেখবেন বইটা আছে কিনা। +- যদি থাকে দাম জিজ্ঞেস করে যদি ২০০ টাকার মধ্যে হয় তাহলে কিনবেন। +- কিনে এরপর আবার নীলক্ষেত মোড়ে আসবেন। +- এসে ১০নাম্বার বাসে উঠে আগের মতো ফার্মগেইট চত্ত্বরে নামবেন। +- এরপর আমার বাসায় বইটা নিয়ে আসবেন। + এবার সংজ্ঞা থেকে বুঝুন ইম্পেরেটিভ প্যারাডাইমে প্রোগ্রামার মেশিনকে সুনির্দিষ্ট নির্দেশনা দিয়ে দিচ্ছে কিভাবে কী করতে হবে। এটাকে বলে ইম্পেরেটিভ প্যারাডাইম। + +### Imperative Paradigm + +ধরেন কেউ গ্রাম থেকে আসছে। সে ঢাকা চেনে না। তাকে স্টেপ বাই স্টেপ ইনস্ট্রাকশন দিয়ে যেতে হবে। মানে মেশিনের খুব কাছাকাছি ল্যাঙ্গুয়েজ যেগুলো সেগুলোকে ইনস্ট্রাক্টশন দিয়ে যেতে হবে লাইন বাই লাইন। সেক্ষেত্রে সি, সি++, জাভা এগুলোকে বলে লো লেভেল ল্যাঙ্গুয়েজ মানে মেশিনের খুব কাছাকাছি। জেনারেশনের দিক দিয়ে ভাবলে এগুলো সবই হাই লেভেল। কিন্তু যেহেতু এগুলো সব মেশিনের কাছাকাছি তাই এদের লো লেভেল ল্যাঙ্গুয়েজ হিসেবে গণ্য করার যায়। এগুলো উপর ভিত্তি করে ইম্পেরেটিভ প্যারাডাইমের ল্যাঙ্গুয়েজকে দুইভাগে ভাগ করা যায়- প্রোসিডিউরাল প্রোগ্রামিং এবং অবজেক্ট অরিয়েন্টেড প্রোগ্রামিং। + +**প্রোসিডিউরাল** - প্রোসিডিউরাল হলো কোনো গ্রুপ নাই, স্টেট নাই, কারো সাথে কারো সম্পর্ক নাই, একটা ফাইল ক্রিয়েট করে ইনস্ট্রাকশন লেখা শুরু করলাম টপ টু বটম। এটাই হচ্ছে প্রোসিডিউরাল। উইকিপিডিয়ার ভাষায় - `which groups instructions into procedures`। + +**অবজেক্ট অরিয়েন্টেড** - আমরা আগের মতোই একটা ফাইল ক্রিয়েট করবো। প্রোসিডিউরালে শুধু আমরা বিভিন্ন ভ্যারিয়েবল ডিক্লেয়ার করে বর্ণনা করেছি। কোনো লজিক্যাল গ্রুপ ছিল না। অবজেক্ট অরিয়েন্টেডেও আমরা বর্ণনা দিবো। কিন্তু লজিক্যাল গ্রুপে ভাগ করে। এখন আগের উদাহরণই যদি আমরা ধরি, তাহলে আমরা সেখানে বই কেনার জন্য একটা ক্লাস তৈরি করতে পারি। যেমন বই কোথায় পাওয়া যাবে, কোন বই, কিভাবে দাম করতে হবে ইত্যাদি। এরপর আমরা ট্রান্সপোর্টেশনের জন্য আমরা একটা ক্লাস বানাবো। কিভাবে ফার্মগেইট থেকে নীলক্ষেত যেতে হবে সেই ইনস্ট্রাকশন দিয়ে। এই যে দুইটা ভিন্ন ক্লাস বানালাম সেখানে একটা ক্লাসের অন্য ক্লাসের ইনফরমেশনের কোনো দরকার নেই। দুইটা ভিন্ন ভিন্ন গ্রুপ। অবজেক্ট অরিয়েন্টেডের সুবিধা হলো ক্লাসগুলো আমরা পুনরায় ব্যবহার করতে পারি। এখন ক এবং খ দুইজন ব্যক্তি। ক ঢাকায় থাকে, খ গ্রামে থাকে। এখন ক এর জন্য আমার ট্রান্সপোর্টেশনের ক্লাসটা সম্পূর্ণ ইনভ্যালিড। কারণ তাকে ঢাকা চেনাতে হচ্ছে না। তাকে শুধু বইয়ের ইনস্ট্রাকশন দিলেই হচ্ছে। সেক্ষেত্রে শুধু বইয়ের ক্লাস কাজে লাগবে। কিন্তু খ এর ক্ষেত্রে ট্রান্সপোর্টেশন এবং বই দুইটা ক্লাসই দরকার হবে। কারণ সে ঢাকা চেনে না। এক্ষেত্রে বইয়ের ক্লাস পুনরায় ইউজ করা যাচ্ছে। যদি প্রোসিডিউরাল হতো তাহলে ক এর জন্য কিছু কোড যুক্তিহীন হয়ে যাবে, কারণ তার জন্য ট্রান্সপোর্টেশনের কোড খাটবে না। আবার ধরা যাক গ ঢাকার বাইরে থাকেন, কিন্তু নীলক্ষেতে উনার পরিচিত মানুষ আছেন। তাহলে তার ক্ষেত্রে বই কেনার ইন্সট্রাকশন প্রয়োজন নাই। তার দরকার শুধু ট্রান্সপোর্টেশনের ইন্সট্রাকশন। তাহলে আপনারা বুঝতে পারছেন যে অবজেক্ট অরিয়েন্টেডের কি কি সুবিধা। + +এখন যদি আপনি অবজেক্ট অরিয়েন্টেড ভালভাবে বুঝে নেন, ল্যাঙ্গুয়েজ স্পেসিফিকভাবে না, তাহলে যেকোনো ল্যাঙ্গুয়েজেই আমরা অবজেক্ট অরিয়েন্টেড অ্যাপ্লাই করতে পারবে। ল্যাঙ্গুয়েজ কোনো বিষয় না। থিওরি বুঝলে আপনি যেকোনো ল্যাঙ্গুয়েজেই ঐ থিওরি অ্যাপ্লাই করতে পারবেন। শুধু সিনট্যাক্স শিখলেই হবে। + +### Declarative Paradigm + +ডেক্লারেটিভ প্রোগ্রামিং হলো আগে যেটা বলেছিলাম শুধু বই কিনে নিয়ে আসতে বলবো। এবার সে কিভাবে যাবে, কিভাবে দরাদরি করবে সেটা তার বিষয়। আমার তাতে কোনো মাথাব্যাথা নেই। আমার বই পেলেই হলো। + +ডেক্লারেটিভ প্যারাডাইমে আছে ফাংশনাল প্রোগ্রামিং, লজিক প্রোগ্রামিং, ম্যাথমেটিক্যাল প্রোগ্রামিং এবং রিয়্যাক্টিভ প্রোগ্রামিং। + +- লজিক প্রোগ্রামিংঃ সিস্টেম ডিজাইন রিলেটেড কোনো কাজ থাকলে এটা নিয়ে কাজ করা হয়। উইকিপিডিয়ার ভাষায়, `in which the desired result is declared as the answer to a question about a system of facts and rules` +- ম্যাথমেটিক্যাল প্রোগ্রামিংঃ এটা তারা ব্যবহার করে যারা অপটিমাইজ সল্যুশন আনার জন্য কাজ করেন। উইকিপিডিয়ার ভাষায়, `in which the desired result is declared as the solution of an optimization problem` + +ডেভেলপমেন্টের জন্য এই দুইটা জিনিস আমাদের কখনও দরকার হয় না। তার জন্য দরকার ফাংশনাল এবং কিছু কিছু ক্ষেত্রে রিয়্যাক্টিভ। + +- ফাংশনাল প্রোগ্রামিংঃ এটা অনেকটা অবজেক্ট অরিয়েন্টেডের মতোই। কিন্তু অবজেক্ট অরিয়েন্টেডে আমরা যেভাবে ডিটেইলস ইন্সট্রাকশন লিখে রাখি একটা গ্রুপের জন্য এখানে সেটা তা আমাদের জন্য কেউ না কেউ আগেই লিখে রেখেছে। আমাদের কাজ হচ্ছে জাস্ট কি করতে হবে সেটা কল করা। ডেক্লারেটিভ প্যারাডাইমের বেলায় একটা জিনিস খেয়াল রাখতে হবে আমাদের জন্য ইন্সট্রাকশন কেউ ইম্পেরেটিভ ওয়েতে লিখে রেখেছে আমরা শুধু তা ইউজ করব। সেজন্যই একে বলে ডেক্লারেটিভ। যেমন আমরা যখন `array.map()` বা `array.reduce()` ব্যবহার করি আমরা জানিনা এর পিছনের কোডটা কিভাবে লেখা আছে। আমাদের জানতেও হবে না। আমরা শুধু ডিক্লেয়ার করবো। উইকিপিডিয়ার ভাষায়, `in which the desired result is declared as the value of a series of function applications`। +- রিয়্যাক্টিভ প্রোগ্রামিংঃ এটা মূলত ব্যবহার করা হয় Asynchronous টাস্কের ক্ষেত্রে। উইকিপিডিয়ার ভাষায়, `in which the desired result is declared with data streams and the propagation of change`। এখানে যে data streams and the propagation of change এর কথা বলা হচ্ছে এগুলো সবই Asynchronous tasks. যেমন আপনি কোনো একটা বাটনে ক্লিক করলেন। সেটার জন্য একটা রেসপন্স পেলেন, সেই রেসপন্সের কারণে একটা জায়গার স্টেট চেইঞ্জ হলো। সেটার ফলে আরো দশটা জায়গায় চেইঞ্জ হলো। এটা হলো রিয়্যাক্টিভ প্রোগ্রামিং। svelte, vue, angular এরা সবই রিয়্যাক্টিভ ওয়েতে কাজ করে। কিন্তু react ভিন্ন ওয়েতে কাজ করে। সেটা অন্য কোনো জায়গায় আলোচনা করা হবে। রিয়্যাকশনের মানে হলো কিছু একটা ঘটার কারণে অন্য কোনো জায়গায় ডাটা চেইঞ্জ হচ্ছে। এটাই রিয়্যাক্টিভ প্রোগ্রামিং এর কনসেপ্ট। আর এটাও ডেক্লারেটিভ, কারণ আমার বিহাইন্ড দ্য সীন কি হচ্ছে জানার দরকার নেই। আমি শুধু ডিক্লেয়ার করবো। + +এইচটিএমএলও ডেক্লারেটিভ প্যারাডাইমের মধ্যে পড়ে। কারণ আমি `h1` ট্যাগ লেখার পর কোনোভাবেই বলে দিচ্ছি না তা কিভাবে কাজ করতে হবে। আমি জাস্ট ট্যাগটা লিখলাম। বাকিটা বিহাইন্ড দ্য সীন হয়ে যাচ্ছে। আরো ডিটেইলসের জন্য এই [লিংক](https://en.wikipedia.org/wiki/Declarative_programming#Subparadigms) ভিজিট করতে পারেন। + +
+ +এখন প্রশ্ন হলো জাভাস্ক্রিপ্ট, পাইথন এগুলো কোন ধরণের ল্যাঙ্গুয়েজ। এই বিষয় জানার আগে আমাদের একটু এই [আর্টিকেল](https://en.wikipedia.org/wiki/List_of_programming_languages_by_type) এ যেতে হবে। এটা একটা লিস্ট। ডিফারেন্ট প্রোগ্রামিং ল্যাঙ্গুয়েজ আছে ডিফারেন্ট টাইপের। যদিও এত ল্যাঙ্গুয়েজ আমাদের ডেভেলপমেন্টের জন্য লাগে না। তাও একবার চোখ বুলিয়ে নেয়া দরকার। কত টাইপের ল্যাঙ্গুয়েজ আছে, কোন ল্যাঙ্গুয়েজ কোন টাইপের মধ্যে পড়ে এসব। + +এখন এখানে খেয়াল করলে দেখা যাবে যে, সি, সি++, সি#, জাভাস্ক্রিপ্ট, পাইথন এগুলো সব ইম্পেরেটিভ ল্যাঙ্গুয়েজের মধ্যে দেয়া আছে। + +আবার এগুলো ফাংশনাল প্রোগ্রামিং এর মধ্যে আছে। ঘটনাটা পুরোই কনফিউজিং। + +ফাংশনাল প্রোগ্রামিং ল্যাঙ্গুয়েজ দুই প্রকার। পিওর এবং ইম্পিওর। এ সমস্ত ল্যাঙ্গুয়েজ ইম্পিওর ফাংশনাল প্রোগ্রামিং ল্যাঙ্গুয়েজের মধ্যে আছে। আগে আমাদের বুঝতে হবে তাহলে পিওর এবং ইম্পিওর ফাংশনাল প্রোগ্রামিং ল্যাঙ্গুয়েজ বলতে কী বোঝায়? + +- ইম্পিওর ফাংশনাল ল্যাঙ্গুয়েজঃ এসব ল্যাঙ্গুয়েজ চাইলে আমরা ফাংশনাল, অবজেক্ট অরিয়েন্টেড, রিয়্যাক্টিভ যেকোনো ওয়েতে ব্যবহার করতে পারি। আমাদের যা ইচ্ছা আমরা সেভাবে ব্যবহার করবো। এজন্য এগুলো ইম্পিওর ফাংশনাল ল্যাঙ্গুয়েজ। মানে আমরা ফাংশনালের বাইরেও অন্যভাবে এদের ইউজ করতে পারি +- পিওর ফাংশনাল ল্যাঙ্গুয়েজঃ এগুলো পুরোপুরিই ফাংশনাল। + +তার মানে আমাদের কোন ল্যাঙ্গুয়েজে এগুলো কিভাবে অ্যাপ্লাই করবো তা নিয়ে ভাবার দরকার নাই। আমাদের দরকার শুধু এগুলো কিভাবে কাজ করে তার কনসেপ্ট এবং যেকোনো একটা ল্যাঙ্গুয়েজে এগুলোর ইমপ্লিমেন্টেশন। যদি তা পারি তাহলে যেকোনো ল্যাঙ্গুয়েজে গিয়ে আমরা এগুলো অ্যাপ্লাই করতে পারবো। জাস্ট শুধু সিনট্যাক্স শিখলেই হবে। তবে আপনি ৫ দিন ধরে গোল্যাং দেখে, ৫ বছর ধরে যিনি গোল্যাং নিয়ে কাজ করছে তার মতো করতে পারবেন না। কিন্তু প্রব্লেম সলভ করতে পারবেন ডেফিনিটলি। + +এখন এই প্রোগ্রামিং প্যারাডাইম থেকে আমরা দেখলাম মূলত ৩ ধরণের প্রব্লেম সলভ করার জন্য বিভিন্ন ল্যাঙ্গুয়েজ এসেছে। যখন কিছুই ছিল না তখন প্রোসিডিউরাল ওয়েতে কোড লেখার জন্য আসলো সি। এরপর যখন কোড রিইউজের দরকার পড়লো, এন্টারপ্রাইজ লেভেলের অ্যাপ্লিকেশন তৈরির জন্য তখন আসলো জাভা, সি++ বা সহজ ভাষায় অবজেক্ট অরিয়েন্টেড প্রোগ্রামিং। এটা আমাদের অনেক প্রব্লেম সলভ করছে। আজকের দুনিয়ায় আমরা যেস বড় বড় অ্যাপ্লিকেশন দেখতে পারি এগুলো সবই হচ্ছে OOP এর ফল। এখন যেহেতু OOP ইম্পেরেটিভ প্যারাডাইম, তাই আমাদের প্রতিটা কোড লিখে লিখে ইন্সট্রাকশন দিতে হয়। সেক্ষেত্রে অনেক কোড লিখতে হয়। এই সমস্যার সমাধান করতে এসেছে ডেক্লারেটিভ প্যারাডাইমের ল্যাঙ্গুয়েজগুলো। যদিও কেউ না কেউ আমাদের জন্য লিখে রেখেছে, কিন্তু আমরা শুধু কমান্ড দিলেই কাজ হয়ে যাচ্ছে অনেক কম কোড লিখে। তাই এখন পাইথন, জাভাস্ক্রিপ্টের মতো ল্যাঙ্গুয়েজগুলো খুবই জনপ্রিয়। + +এখন বুঝলাম যে ৩ ধরণের প্রব্লেম সলভ করার জন্য ল্যাঙ্গুয়েজগুলো আসছে। তাহলে ৩টা ল্যাঙ্গুয়েজ থাকলেই তো হতো। এত এত ল্যাঙ্গুয়েজ কেন? সি এর সিনট্যাক্সের সাথে তো সি++ পুরোই মিলে, জাভা, জাভাস্ক্রিপ্টের কিছু কিছু মিলে। তাহলে কি এমন দরকার পড়লো যে আমাদের নতুন ল্যাঙ্গুয়েজ ক্রিয়েট করতে হলো? এই জায়গায় আমরা বিগিনাররা একটা ভুল করে ফেলি কারণ আমরা সিনট্যাক্স দিয়ে একটা ল্যাঙ্গুয়েজ বিচার করি। সিনট্যাক্স হলো আপনার কম্পিউটারের কীবোর্ডের মতো। এখন কোন কীবোর্ডে লাইট জ্বলে, কোনোটাতে জ্বলে না, কোনোটা মেকানিক্যাল, কোনোটা সেমিমেকানিক্যাল, কোনোটাতে নামপ্যাড আছে, কোনোটাতে নেই, কোনোটাতে মিডিয়া প্লেয়ার বাটন আছে ইত্যাদি। কিন্তু কীবোর্ডের কাজটা কিন্তু সেইম। নরমাল কীবোর্ড যেগুলো সেগুলো দেখলে দেখা যাবে একই রকম। শুধু কিছু ফিচার কম বেশি। তাহলে একটা কীবোর্ড দিলেই তো হয়ে যেতো। বাজারে এত কীবোর্ড কেন? সেরকম সিনট্যাক্স হচ্ছে অনেকটা কীবোর্ডের কী এর মতো। কিছু কমবেশি থাকতে পারে কীবোর্ড ভেদে, কিন্তু ম্যাক্সিমাম কী সেইম, তার ফাংশনালিটিজও সেইম। সেরকম সিনট্যাক্সও ল্যাঙ্গুয়েজ ভেদে মিল থাকতে পারে। মূল বিষয় হচ্ছে ইমপ্লিমেন্টেশন। আমি কিভাবে ইমপ্লিমেন্ট করছি কোনো ল্যাঙ্গুয়েজ। ইমপ্লিমেন্ট বলতে বুঝাচ্ছে এই ল্যাঙ্গুয়েজটা যারা তৈরি করেছে তারা কি ভেবে তৈরি করেছে। তাদের কম্পাইলার তারা কিভাবে ডিজাইন করেছে। এটা হচ্ছে ব্যাপার। কম্পাইলার অনেকভাবে ডিজাইন করা যায়। এই কম্পাইলারের ডিজাইন এর উপর ডিপেন্ড করে আমার প্রোগ্রাম কত কম সময়ে রান করবে। এই এক্সিকিউশন টাইম, মেমোরি, গারবেজ কালেকশন মানে কপ্লাইলার আর্কিটেকচারের উপর ভিত্তি করেই এই প্রোগ্রামিং ল্যাঙ্গুয়েজগুলো চেইঞ্জ হয়। আরেকটা বিষয় আছে সেটা হলো কনকারেন্সি। কনকারেন্সি মানে হলো ধরেন আপনার কম্পিউটারে ৮টা কোর আছে। আপনি সেই ৮টি কোর ব্যবহার করেই একই সময়ে কোড রান করাতে চাচ্ছেন। এটাই কনকারেন্সি। আগে কম্পিউটার প্রসেসর ছিল সিঙ্গেল কোর। এখন মাল্টিকোর। ৩০ বছর আগের কম্পিউটারের প্রসেসিং পাওয়ার আর আজকের কম্পিউটারের প্রসেসিং পাওয়ার তো এক না। সেক্ষেত্রে কম্পাইলার বারবার চেইঞ্জ করতে হয়েছে, আপগ্রেড করতে হয়েছে, ক্ষেত্রবিশেষে নতুন ল্যাঙ্গুয়েজ তৈরি করতে হয়েছে। যেমন সি কিন্তু গারবেজ কালেক্টর না। গারবেজ কালেক্টর বলতে বুঝায় আমরা যে ভ্যারিয়েবলের মধ্যে ডেটা রাখলাম, এ্যারে তৈরি করলাম, মেমোরিতে কিছু স্টোর করলাম, এগুলো নির্দিষ্ট সময় পরপর ক্লিন করতে হয়। সি সেটা পারে না। সেজন্য আমাদের নতুন করে মেমোরি কিভাবে কাজ করছে সেটা শিখতে হচ্ছে, সেভাবে মেমোরি ক্লিন করতে হচ্ছে। এরপর যখন আমরা জাভাতে আসলাম সেখানে কিন্তু আমাদের আর গারবেজ কালেক্ট করতে হচ্ছে না। সে অটোমেটিক গারবেজ কালেক্ট করে ক্লিন করে ফেলে। সুতরাং অনেক কিছুর উপর ভিত্তি করে প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি হয়, তেমনি কম্পাইলার ডিজাইনের উপর ভিত্তি করে ভিন্ন ভিন্ন প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি হয়েছে। একেকটা নির্দিষ্ট কাজের জন্য একেকটা প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি হয়েছে। কম্পাইলারের উপর ভিত্তি করে মূলত ৩ ধরণের প্রোগ্রামিং ল্যাঙ্গুয়েজ তৈরি হয়েছে। এরা হলো কম্পাইল্ড, ইন্টারপ্রেটেড, জাস্ট ইন টাইম (JIT) + +- কম্পাইল্ডঃ কম্পাইল্ড হলো আমি যে কোড লিখেছি সেটা পিওর মেশিন কোডে রূপান্তরিত হবে। মেশিন কোড বলতে বুঝানো হচ্ছে বাইনারি কোড। কম্পাইল্ড ল্যাঙ্গুয়েজগুলো বেশি ফাস্ট হয়। সি, সি++ এরা হলো কম্পাইল্ড। জাভা এক হিসাবে কম্পাইল্ড। কিন্তু পিওর কম্পাইল্ড না। কারণ মাঝখানে জেভিএম নামে একটা ভার্চুয়াল মেশিন আছে যেটাতে জাভা রান হয়। তাই জাভা সি++ এর চেয়ে অনেক স্লো। সি এর থেকে আরো স্লো। কম্পাইল্ড ল্যাঙ্গুয়েজের স্টেপ ৩টা। প্রথমে আপনি কোড লিখবেন, এরপর আপনি বিল্ড করবেন exe বা class ফাইলে, এরপর আপনি রান করবেন। এতে আপনার কম্পাইলে টাইম লাগবে কিন্তু এক্সিকিউশনে খুব কম টাইম লাগছে। তাই প্রোগ্রাম অনেক দ্রুত রান হয়। আরেকটা সুবিধা হলো কম্পাইল করার সময় যদি আপনার কোডে কোনো ভুল থাকে সে আপনাকে ধরিয়ে দেবে। এটাকে বলে কম্পাইল টাইম এরর। এগুলোতে বুট করতে টাইম বেশি লাগে, যেহেতু একটা ইন্টারমিডিয়েট প্রসেস আছে কম্পাইল করতে, কিন্তু এক্সিকিউশনে টাইম কম লাগে। +- ইন্টারপ্রেটেডঃ এর কাজ হলো এ শুরুতেই কোড রান করে দিবে। এরপর যখন যেটা লাগবে সেটা সেভাবে রীড করে সে অনুযায়ী এক্সিকিউট করবে। যেমন পাইথন। ধরেন আপনি পাইথন কোড লিখলেন, রান বাটনে ক্লিক করলেন, সে লাইন বাই লাইন পড়বে না। তাই পুরো কোডের কোথাও যদি কোথাও ভুল থাকে সে রান করে ফেলবে, সে ধরতে পারবে না কারণ সে তো কম্পাইলই করছে না। যখন দরকার তখন সে সেই কোড পড়ে সেটা কম্পাইল করে মেশিনকে বুঝিয়ে দী। তাই এগুলোতে রানটাইম এরর বেশি পাওয়া যায়। এ সমস্ত ল্যাঙ্গুয়েজের অসুবিধা হলো এরা একটা কোড বারবার কম্পাইল করবে। তাই আমার এক্সিকিউশন টাইম অনেক বেশি লাগবে। সে কারণে ইন্টারপ্রেটেড ল্যাঙ্গুয়েজগুলো অনেক স্লো হয় কম্পাইল্ড ল্যাঙ্গুয়েজের তুলনায়। এগুলোতে বুট করতে টাইম কম লাগে, কিন্তু এক্সিকিউশনে টাইম বেশি লাগে। +- JIT: এক্ষেত্রে ফাইলটা ইনস্ট্যান্টলি রীড করে ফেলবে। এরপর যখন যা দরকার হবে তখন সে ইন্টারপ্রেট করবে না, সরাসরি কম্পাইল করে মেশিন কোডে রূপান্তর করে ফেলবে। তাহলে কোডগুলো থেকে যাচ্ছে, বারবার আর কম্পাইল করতে হচ্ছে না। আমি রিইউজ করতে পারছি। এটাই বেসিক কনসেপ্ট। + +জাভাস্ক্রিপ্ট একটা সময় ইন্টারপ্রেটেড ছিল। জাভাস্ক্রিপ্টের এই রেভ্যুলিউশনের জন্য দুইজন লোককে ক্রেডিট দিতে হয়। একজন হলেন সুন্দর পিচাই। তিনি যদি v8 engine এবং গুগল ক্রোম তৈরির কথা না ভাবতেন তাহলে আজকে জাভাস্ক্রিপ্টের এই জয়জোয়ার আসতো না। জাভাস্ক্রিপ্টকে ইন্টারপ্রেটেড থেকে JIT কম্পাইলারে নিয়ে আসার পিছনে কৃতিত্ব হচ্ছে গুগল এবং ঐ সময় তার লীডে থাকা সুন্দর পিচাই। দ্বিতীয় জন হলেন রায়ান ডেল। ঐ v8 এর উপর ভিত্তি করে তিনি জাভাস্ক্রিপ্টকে ব্রাউজারের বাইরে নিয়ে আসেন। এই JIT কম্পাইলারের কারণে জাভাস্ক্রিপ্টের ইতিহাস পালটে গেছে। কারণ একই ল্যাঙ্গুয়েজ যদি আপনি দুই জায়গায় ব্যবহার করতে চান তাহলে সেই ল্যাঙ্গুয়েজকে অনেক দ্রুত এক্সিকিউট হতে হবে। আগে সেটা জাভাস্ক্রিপ্টের ছিল না। কিন্তু v8 ইঞ্জিন আসার পর তা সম্ভব হয়েছে। এই সুযোগ আসায় জাভাস্ক্রিপ্ট দিয়ে অনেক বড় অ্যাপ্লিকেশন তৈরি করা সম্ভব হয়েছে। + +গো ল্যাং এর ইতিহাস যদি আমরা দেখি, সি এর কো-অথর কেন থম্পসন চিন্তা করলেন বড় অ্যাপ্লিকেশন তৈরির জন্য সি এর সিনট্যাক্সগুলো খুব ভাল না। তাই সি খুব সুবিধা করতে পারবে না। তাই আমাদের সি এর মতো সেইম পারফরম্যান্স থাকতে হবে, কিন্তু সহজে কোড করা যায়, গারবেজ কালেক্ট করা যায়, কনকারেন্সির বিষয়বস্তু সহজ এরকম একটা ল্যাঙ্গুয়েজ আনতে হবে। গো নামটা এসেছে গো রুটিন থেকে। গো রুটিনে কনকারেন্সি এত সহজ তা কল্পনা করা যায় না। সবদিক বিবেচনা করে তিনি গো নামক একটি লো লেভেল ল্যাঙ্গুয়েজ তৈরি করলেন যা আমাদের কাছে গো ল্যাং নামে পরিচিত। এটা একটা গেইম চেইঞ্জার। কারণ আমরা পাওয়ার পাচ্ছি সি এর মতো, কিন্তু কোড লিখছি পাইথনের মতো সহজ। + +তাহলে দেখেন যে সি জানে সে গো তে এসে কি আটকে যাওয়ার কথা? এত এত ল্যাঙ্গুয়েজ আসার পিছনে কারণ হচ্ছে কোনো একটা প্রব্লেম আমি এই ল্যাঙ্গুয়েজ দিয়ে সলভ করতে পারছি না সেজন্য নতুন একটা ল্যাঙ্গুয়েজ এসেছে। সি অনেক বছর আগের। তখন সেভাবে প্রব্লেম সলভ হতো। কিন্তু এখনকার দ্রুত দুনিয়ায় খাপ খাওয়াতে গো ল্যাং এর আবির্ভাব। মেইন রিজন হচ্ছে এটাই। কেউ কারো প্রতিদ্বন্ধী না। প্রত্যেকেই প্রত্যেকের উত্তরসূরী বা পূর্বসূরী। + +এমন ডার্টের কথা বলি। সব ল্যাঙ্গুয়েজের বেস্ট ফিচার্স নিয়ে তারা একটা ল্যাঙ্গুয়েজ তৈরি করেছে সহজে অ্যাপ্লিকেশন ডেভেলপমেন্টের জন্য যার নাম দিয়েছে ডার্ট। + +আরেকটা প্রশ্ন আসতে পারে এত আলোচনার পর। নতুন ল্যাঙ্গুয়েজ সৃষ্টি না করে কি বর্তমান ল্যাঙ্গুয়েজকে আপগ্রেড করা যেতো না? অবশ্যই যেতো, এবং করা হয়ও। ধরেন আপনি ৩০ বছর আগে ১৫ তলা একটা বিল্ডিং তৈরি করলেন। এখন আপনি কালার চেইঞ্জ করা, দরজা, জানালা চেইঞ্জ করা ছাড়া বর্তমান জমানার বিল্ডিং এ কী কনভার্ট করতে পারবেন? সম্ভব না। কারণ ৩০ বছর আগে সেটা তৈরি হয়েছিল তখনকার আর্কিটেকচার দিয়ে। এখন সেই পুরনো আর্কিটেকচার ভেঙেচুরে নতুন আর্কিটেকচারের বিল্ডিং বানানো সম্ভব না। যেমন নোড জেএস অল্প কিছদিন হলো এসেছে, তার আর্কিটেকচারই ভাঙতে পারলো না, ডেনো আনতে হলো। এটাই হলো মূল ব্যাপার + +## শেষ কথা + +আমাদেরকে শিখতে হবে থিওরি। অবজেক্ট অরিয়েন্টেড, ফাংশনাল প্রোগ্রামিং, গারবেজ কালেকশন, কনকারেন্সি এসব আমরা যখন জানবো এবং যেকোনো একটা ল্যাঙ্গুয়েজ দিয়ে ইমপ্লিমেন্ট করতে পারবো অন্য যেকোনো ল্যাঙ্গুয়েজে গিয়ে আমাদের কষ্ট করতে হবে না। +আমরা যেকোনো কিছু শিখতে গেলে ল্যাঙ্গুয়েজ স্পেসিফিক হবো না, ফ্রেমওয়ার্ক স্পেসিফিক হবো না। আমরা চেষ্টা করবো abstractly তার কোর নলেজ শেখার জন্য। এরপর আমরা চেষ্টা করবো যে ল্যাঙ্গুয়েজে আছি তাতে কিভাবে অ্যাপ্লাই করবো। + +## Resource for this lecture + +**এই লেকচারের সকল রিসোর্স পাওয়ার জন্য ক্লিক করুন [এখানে](../../resources/lecture-02/README.md)** + +## Author + +- [Aditya Chakraborty](https://github.com/adityackr) diff --git a/class-overview/Lecture-01/README.md b/full-stack-army/class-overview/Lecture-01/README.md similarity index 100% rename from class-overview/Lecture-01/README.md rename to full-stack-army/class-overview/Lecture-01/README.md diff --git a/full-stack-army/class-overview/Lecture-01/Waterfall_model.png b/full-stack-army/class-overview/Lecture-01/Waterfall_model.png new file mode 100644 index 0000000..355b2eb Binary files /dev/null and b/full-stack-army/class-overview/Lecture-01/Waterfall_model.png differ diff --git a/full-stack-army/class-overview/Lecture-01/agile-model-of-se.png b/full-stack-army/class-overview/Lecture-01/agile-model-of-se.png new file mode 100644 index 0000000..c1c8c65 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-01/agile-model-of-se.png differ diff --git a/full-stack-army/class-overview/Lecture-01/devops.png b/full-stack-army/class-overview/Lecture-01/devops.png new file mode 100644 index 0000000..2ae92b0 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-01/devops.png differ diff --git a/class-overview/Lecture-02/README.md b/full-stack-army/class-overview/Lecture-02/README.md similarity index 100% rename from class-overview/Lecture-02/README.md rename to full-stack-army/class-overview/Lecture-02/README.md diff --git a/resources/lecture-03/Programming Language Landscape.png b/full-stack-army/class-overview/Lecture-03/Programming Language Landscape.png similarity index 100% rename from resources/lecture-03/Programming Language Landscape.png rename to full-stack-army/class-overview/Lecture-03/Programming Language Landscape.png diff --git a/class-overview/Lecture-03/README.md b/full-stack-army/class-overview/Lecture-03/README.md similarity index 100% rename from class-overview/Lecture-03/README.md rename to full-stack-army/class-overview/Lecture-03/README.md diff --git a/class-overview/Lecture-04/README.md b/full-stack-army/class-overview/Lecture-04/README.md similarity index 100% rename from class-overview/Lecture-04/README.md rename to full-stack-army/class-overview/Lecture-04/README.md diff --git a/class-overview/Lecture-05-06/README.md b/full-stack-army/class-overview/Lecture-05-06/README.md similarity index 100% rename from class-overview/Lecture-05-06/README.md rename to full-stack-army/class-overview/Lecture-05-06/README.md diff --git a/full-stack-army/class-overview/Lecture-05-06/Screenshot_1.png b/full-stack-army/class-overview/Lecture-05-06/Screenshot_1.png new file mode 100644 index 0000000..7b87186 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-05-06/Screenshot_1.png differ diff --git a/class-overview/Lecture-07/README.md b/full-stack-army/class-overview/Lecture-07/README.md similarity index 100% rename from class-overview/Lecture-07/README.md rename to full-stack-army/class-overview/Lecture-07/README.md diff --git a/class-overview/Lecture-08/README.md b/full-stack-army/class-overview/Lecture-08/README.md similarity index 100% rename from class-overview/Lecture-08/README.md rename to full-stack-army/class-overview/Lecture-08/README.md diff --git a/class-overview/Lecture-09/README.md b/full-stack-army/class-overview/Lecture-09/README.md similarity index 100% rename from class-overview/Lecture-09/README.md rename to full-stack-army/class-overview/Lecture-09/README.md diff --git a/class-overview/Lecture-10/README.md b/full-stack-army/class-overview/Lecture-10/README.md similarity index 100% rename from class-overview/Lecture-10/README.md rename to full-stack-army/class-overview/Lecture-10/README.md diff --git a/full-stack-army/class-overview/Lecture-10/Screenshot_1.png b/full-stack-army/class-overview/Lecture-10/Screenshot_1.png new file mode 100644 index 0000000..bba883d Binary files /dev/null and b/full-stack-army/class-overview/Lecture-10/Screenshot_1.png differ diff --git a/full-stack-army/class-overview/Lecture-10/Screenshot_2.png b/full-stack-army/class-overview/Lecture-10/Screenshot_2.png new file mode 100644 index 0000000..b0129d2 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-10/Screenshot_2.png differ diff --git a/full-stack-army/class-overview/Lecture-10/Screenshot_3.png b/full-stack-army/class-overview/Lecture-10/Screenshot_3.png new file mode 100644 index 0000000..7b779ad Binary files /dev/null and b/full-stack-army/class-overview/Lecture-10/Screenshot_3.png differ diff --git a/full-stack-army/class-overview/Lecture-10/event-loop.gif b/full-stack-army/class-overview/Lecture-10/event-loop.gif new file mode 100644 index 0000000..5cae5b0 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-10/event-loop.gif differ diff --git a/class-overview/Lecture-11/README.md b/full-stack-army/class-overview/Lecture-11/README.md similarity index 100% rename from class-overview/Lecture-11/README.md rename to full-stack-army/class-overview/Lecture-11/README.md diff --git a/full-stack-army/class-overview/Lecture-11/Screenshot_1.png b/full-stack-army/class-overview/Lecture-11/Screenshot_1.png new file mode 100644 index 0000000..47fc090 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-11/Screenshot_1.png differ diff --git a/class-overview/Lecture-12/README.md b/full-stack-army/class-overview/Lecture-12/README.md similarity index 100% rename from class-overview/Lecture-12/README.md rename to full-stack-army/class-overview/Lecture-12/README.md diff --git a/full-stack-army/class-overview/Lecture-12/decision-tree.jpg b/full-stack-army/class-overview/Lecture-12/decision-tree.jpg new file mode 100644 index 0000000..556e1ad Binary files /dev/null and b/full-stack-army/class-overview/Lecture-12/decision-tree.jpg differ diff --git a/full-stack-army/class-overview/Lecture-12/disable.jpg b/full-stack-army/class-overview/Lecture-12/disable.jpg new file mode 100644 index 0000000..31e2c72 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-12/disable.jpg differ diff --git a/full-stack-army/class-overview/Lecture-12/enable.jpg b/full-stack-army/class-overview/Lecture-12/enable.jpg new file mode 100644 index 0000000..b594325 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-12/enable.jpg differ diff --git a/full-stack-army/class-overview/Lecture-12/login-process.jpg b/full-stack-army/class-overview/Lecture-12/login-process.jpg new file mode 100644 index 0000000..11a3c2d Binary files /dev/null and b/full-stack-army/class-overview/Lecture-12/login-process.jpg differ diff --git a/full-stack-army/class-overview/Lecture-12/registration-process.jpg b/full-stack-army/class-overview/Lecture-12/registration-process.jpg new file mode 100644 index 0000000..96f7e53 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-12/registration-process.jpg differ diff --git a/full-stack-army/class-overview/Lecture-12/timesheet.jpg b/full-stack-army/class-overview/Lecture-12/timesheet.jpg new file mode 100644 index 0000000..cb1cb3d Binary files /dev/null and b/full-stack-army/class-overview/Lecture-12/timesheet.jpg differ diff --git a/class-overview/Lecture-13/README.md b/full-stack-army/class-overview/Lecture-13/README.md similarity index 100% rename from class-overview/Lecture-13/README.md rename to full-stack-army/class-overview/Lecture-13/README.md diff --git a/full-stack-army/class-overview/Lecture-13/images/Screenshot_2.png b/full-stack-army/class-overview/Lecture-13/images/Screenshot_2.png new file mode 100644 index 0000000..f9d4125 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-13/images/Screenshot_2.png differ diff --git a/class-overview/Lecture-14/README.md b/full-stack-army/class-overview/Lecture-14/README.md similarity index 100% rename from class-overview/Lecture-14/README.md rename to full-stack-army/class-overview/Lecture-14/README.md diff --git a/full-stack-army/class-overview/Lecture-15/README.md b/full-stack-army/class-overview/Lecture-15/README.md new file mode 100644 index 0000000..9a3f963 --- /dev/null +++ b/full-stack-army/class-overview/Lecture-15/README.md @@ -0,0 +1,229 @@ +# Lecture 15 - Backend 2 | Introduction to Backend Development + +ব্যাকএন্ড শুরু করার আগে আমাদের জানতে হবে এর শুরু এবং শেষ কোথায়। শুরু বোঝা যায়। কিন্তু শেষ কখনই বোঝা সম্ভব হয় না যে কোথায় গিয়ে শেষ করতে হবে। মোটামুটি আমাদের একটা লেভেল পর্যন্ত যেতে হবে, যে লেভেল পর্যন্ত গেলে আমরা এরপর থেকে নিজেরাই বাকি জিনিসগুলো শিখে নিতে পারবো। এখন ব্যাকএন্ডের লার্নিং পাথ কেমন হবে? + +ব্যাকএন্ড ডেভেলপারের দায়িত্ব খুব ছোট, আবার খুব বড়। সেটা নির্ভর করছে আপনি কোন কোম্পানিতে জব করছেন তার সাইজের উপর। আপনি যদি নিজের প্রোডাক্ট নিজে ডিজাইন করেন তাহলে ফ্রন্টএন্ড, ব্যাকএন্ড, ডাটাবেজ, API, আর্কিটেকচার, ক্লাউড, সিকিউরিটি, ডকুমেন্টেশন সব নিজেকেই করতে হবে। আপনি যদি ৫/৬ জনের একটা টিমে কাজ করেন তখনও অনেক কাজ করতে হবে। তবে কিছু কিছু ভাগ হয়ে যাবে। আপনি যদি স্টার্টআপ কোম্পানিতে জব করেন যেখানে টিম মোটামুটি বড়, সেক্ষেত্রে আপনার হয়তো ব্যাকএন্ড অ্যাপ্লিকেশন নিয়ে কাজ করতে হচ্ছে বা সিস্টেম ডিজাইন নিয়ে কাজ করতে হচ্ছে। ক্লাউড বা অন্য কিছু নিয়ে কাজ করতে হচ্ছে না। যদি আপনি এন্টারপ্রাইজ কোম্পানিতে জব করেন তখন কাজগুলো অনেক স্পেসিফিক হয়ে যায়। এমনও হতে পারে আপনাকে ডকুমেন্টেশন বানানোর জন্য নেয়া হয়েছে, বা আপনাকে আর্কিটেকচার তৈরির জন্য নিয়েছে, বা টেস্ট কোড লেখার জন্য নিয়েছে, বা API ডিজাইনের জন্য নিয়েছে। তখন কাজ অনেক সহজ হয়ে যায়। + +আমরা এখন নিজেদের বিল্ড করছি। নিজেদের গড়ে তোলার জন্য কোন কোন বিষয়গুলো আমাদের শুরুতে শিখতে হবে তার একটা পরিকল্পনা লাগবে। পরিকল্পনা নিচে দেয়া হলো। + +- API Design: API ডিজাইন বলতে যে জিনিসটা বুঝায় সেটা হলো ডাটা পাস করা। ক্লায়েন্ট এবং সার্ভারের মধ্যে কমিউনিকেশন করার একটা মাধ্যম তৈরি করা। আমরা এই কাজ অনেকভাবে করতে পারি। + - REST API: বেশিরভাগ সময় আমরা API ডিজাইনের জন্য যেটা ব্যবহার করি তা হলো REST API। + - GraphQL: REST API এর পরে আরেকটা জনপ্রিয় টুল আছে, সেটা হলো GraphQL। + - gRPC: তৃতীয় আরেকটা উপায় আছে। সেটা হলো gRPC। + - SOAP: পূর্বে ছিল SOAP। যেটা এখন ধরতে গেলে লাগবেই না। ১% এরও কম ব্যবহার হয় এটা বর্তমানে। এটা লাগবে, যখন আপনি এমন একটা সার্ভিস নিয়ে কাজ করছেন যেটা আজ থেকে ১৫ বছর আগে কাজ করেছিল SOAP নিয়ে, সেক্ষেত্রে আপনার এটা লাগতে পারে। + - Web Socket: এটা রিয়েলটাইম কমিউনিকেশনের জন্য ব্যবহার করা হয়। এটা স্কেল করা অনেক কঠিন। + - Message Broker: এটা দরকার হবে যখন আমরা দুইটা API এর মধ্যে কমিউনিকেশন করবো তখন। এটা আরেক লেভেলের কনসেপ্ট। বিগিনার হিসেবে এটা এখন দরকার নেই। কারণ এটা মোটেও বিগিনার ফ্রেন্ডলি না। +- API Security + - JWT Token + - Refresh Token: এটা খুব হাইলি সিকিউরড সিস্টেমগুলোতে ব্যবহার করা হয়। + - OAuth2: আমরা বিভিন্ন জায়গায় দেখি যে আমাদের জিমেইল, ফেইসবুক, গিটহাব ইত্যাদি একাউন্ট থাকলে আমরা সেগুলো দিয়ে লগইন করতে পারি। সাইনআপ করার প্রয়োজন পড়ে না। OAuth2 দিয়ে আমরা এই সিস্টেম করতে পারি। এটা বিগিনার হিসেবে আমাদের প্রয়োজন নাই। + - SAML (Security Assertion Markup Language): এটা সিঙ্গেল সাইন অনের ক্ষেত্রে বেশি ব্যবহার করা হয়। এটাও আমাদের বিগিনার হিসেবে দরকার নাই। + - Identity Providers: এতে আছে Cognito, Auth0, Firebase, Okta ইত্যাদি। বিগিনার হিসেবে আমাদের এই কনসেপ্টের এখন কোনো প্রয়োজন নাই। + - Role Based Authorization: অথেনটিকেশন আর অথোরাইজেশনের মধ্যে একটা পার্থক্য রয়েছে। সেটা হলো আমি যখন লগইন করলাম, আমি অথেনটিকেট করলাম। আমার আইডি অথেনটিকেশন সিস্টেম পার হয়ে লগইন হলো। এখন লগইন করলেও আমি সব জায়গায় এন্ট্রি নিতে পারবো না। এখানে রোল ভাগ করা। ফেইসবুক গ্রুপের কথা চিন্তা করি। আপনি যখন মেম্বার তখন আপনি শুধু পোস্ট করতে পারবেন, লাইক, কমেন্ট করতে পারবেন, আর শুধু নিজের পোস্ট বা কমেন্ট ডিলিট করতে পারবেন। মডারেটর যারা তারা অন্যের পোস্ট, কমেন্ট বা কোনো মেম্বারকে গ্রুপ থেকে ডিলিট করতে পারলেও অ্যাডমিনকে পারবে না। অ্যাডমিন সব কিছু করতে পারবে। সবকিছুর এক্সেস তার আছে। অ্যাডমিন যাকে যে রোলে অথোরাইজ করেছে সে শুধু সেই রোলই পালন করতে পারবে। এটাকে বলে Role Based Authorization. +- API Testing + - Unit Testing + - Acceptance Testing + - Load Testing +- API Documentation + - Swagger: অটো জেনারেটেড ডকুমেন্টেশনের জন্য আমরা এটা ব্যবহার করে থাকি। + - Postman: আমরা ডেইলি বেসিসে যেটা ব্যবহার করি সেটা হলো পোস্টম্যান। পোস্টম্যানে সুন্দর ডকুমেন্টেশন করা যায় এবং শেয়ারও করা যায়। + +উপরের এই চারটা জিনিস শিখতে গেলে আমাদের কিছু কিছু বিষয় জানতে হবে। সেগুলো হলোঃ + +- Database: ডাটাবেজ সম্পর্কে একদম ইঞ্জিনিয়ার বা অ্যাডমিনিস্ট্রেটর হওয়ার প্রয়োজন নেই। কিন্তু হালকাপাতলা আমাদের ডাটাবেজ সম্পর্কে জানতে হবে। ডাটাবেজের কুয়েরি লেখা অনেক কঠিন কাজ। আমরা কিছু সফটওয়্যার আছে যাদের ORM (Object Relational Mapping) বলে, সেগুলো দিয়ে সুন্দরভাবে ডাটাবেজের কাজ করতে পারবো। অনেকগুলো ডাটাবেজ আছে। তার মধ্যে কিছু কম ডাটাবেজের নাম নিচে দেয়া হলো। + - NoSQL + - MongoDB + - AWS DynamoDB + - SQL + - PostgreSQL + - MySQL + - MSSQL / Oracle + - In Memory: SQL বা NoSQL আমরা আমাদের সুবিধামতো যেটা খুশি সেটা নিয়ে কাজ করতে পারি। কিন্তু In Memory নিয়ে আমাদের মাস্ট কাজ করতে হবে। এটা মূলত ব্যবহার করা হয় ক্যাশিং এর কাজে। যেটা এখন must neede. + - Redis: এটা ক্যাশিং ছাড়াও আরো অনেক ছোট ছোট কাজে ব্যবহার করা যায়। তাই আমাদের প্রেফারেন্স সবসময় রেডিস। + - Mem Cached: এটা শুধুমাত্র ক্যাশিং এর কাজেই ব্যবহার করা হয়। + - Graph Database: এটা এখন ধীরে ধীরে জনপ্রিয় হচ্ছে। তবে বিগিনার হিসেবে আমাদের প্রয়োজন নাই। এই ধরণের ডাটাবেজ ব্যবহার করা হয় বিভিন্ন কমপ্লেক্স টাইপের ডাটা মডেল তৈরি করার ক্ষেত্রে। যেমন মেশিন লার্নিং করে আমরা প্রোডাক্ট রিকমেন্ডেশন করে থাকি। আমরা যদি আমাজনে সার্চ করি কিছু তাহলে ঐ প্রোডাক্ট রিলেটেড কোন কোন প্রোডাক্ট আছে, কোনগুলো মানুষ বেশি কিনে, কোনগুলো জনপ্রিয় এরকম কিছু সাজেশন্স আসে। এগুলো কিছু র‍্যানডম ডাটা। কোনো স্ট্রাকচারেই এগুলো ফেলা যাবে না। এখন প্রতিবার যদি আমাদের বায়িং বিহেভিয়ারকে লার্ন করতে থাকে, তাহলে সেটা অনেক কমপ্লেক্স হয়ে যায়। কারণ মেশিন লার্নিং এর যে সার্ভার খরচ সেটা তুলনামূলক অনেক বেশি। কারণ এখানে প্রচুর কম্পিউটিং পাওয়ার দরকার হয়। তো এখানে কি করা যায় কিছু সময়ের জন্য আমরা আমাদের ডাটাকে একটা ডাটাবেজে সেচ করে রাখতে পারি। ধরেন ২৪ ঘন্টার জন্য। ২৪ ঘন্টা পরে ডাটা চেইঞ্জ হয়ে যাবে, নতুন ডাটা ক্রিয়েট হবে। এরকম র‍্যান্ডম ডাটার ক্ষেত্রে গ্রাফ ডাটাবেজ অনেক কাজ দেয়। + - Neo4j +- Linux Server: ডাটাবেজের পর আমাদের লিনাক্স সার্ভার সম্পর্কে কিছুটা জানতে হয়। +- Cloud Computing: ক্লাউড কম্পিউটিং নিয়ে একটা ধারণা থাকা দরকার। +- DevOps: ডেভঅপ্স সম্পর্কে কিছুটা ধারণা থাকতে হয়। + +Linux Server, Cloud Computing, DevOps এগুলো নিয়ে আমাদের ভাবার প্রয়োজন নেই। আমরা এগুলো এখন ভাববো না। যখন ভাবার ভাববো। + +উপরের এত এত নাম দেখে অনেকের ভয় লাগতে পারে। তাদের উদ্দেশ্যে বলছি, এখানে যা যা আছে সব সহজ। কিন্তু ব্যাকএন্ডের সবচেয়ে কঠিন একটা জিনিস আছে। সেটা হলো রিকোয়ারমেন্টের উপর ভিত্তি করে সিস্টেমটা ডিজাইন করে। ক্লায়েন্ট এমনভাবে একটা রিকোয়ারমেন্ট দিবে যেটা দিয়ে প্রোগ্রামিং এর সাথে মিলানো যাবে না। আমাদেরকে সেটা চুলছেড়া বিশ্লেষন করে সবার মধ্যে সম্পর্ক বের করে সিদ্ধান্ত নিতে হবে আমরা কোনটা কোনটা ব্যবহার করবো। এটাই সবচেয়ে কঠিন কাজ। বাকি যা যা এখানে দেখা যাচ্ছে এগুলো অনেক সহজ। + +এবার আসি যদি আমরা একটা API ডিজাইন করতে চাই তাহলে আমদের কি কি লাগবে? কি প্রোগ্রামিং ল্যাঙ্গুয়েজ আমরা ব্যবহার করবো, কি কি জিনিস হ্যান্ডেল করবো, কিভাবে হ্যান্ডেল করবো সেগুলো নিয়ে। + +আমাদের আগে জানতে হবে সার্ভার কি করে? সার্ভারের কাজ কি? সার্ভারের কাজ সার্ভ করা। কি সার্ভ করা? রিকোয়েস্ট সার্ভ করা। আমরা এবার সার্ভার অ্যাপ্লিকেশন রেসপন্সিবিলিটিজগুলো একটু দেখি। পুরো ব্যাকএন্ডে মূলত তিনটা কাজ। রিকোয়েস্ট লিসেন করা, প্রসেসিং করা এবং রেস্পন্স করা। + +এই তিনটা কাজ করার জন্য আমরা একটা ফ্রেমওয়ার্ক বেছে নিই। কারণ API এর প্রতিটা কাজে আমাদের এই তিনটা জিনিস লাগবে। চলুন আমরা আমাদের প্রথম একটা সার্ভার তৈরি করে ফেলি। + +লেকচার ১৩ তে npm, yarn, package.json, package install এসব নিয়ে আলোচনা করা হয়েছে। তাই আর এগুলো নিয়ে এখানে বলা হচ্ছে না। + +আমরা প্রথমে server.js নামে একটা ফাইল ক্রিয়েট করবো। প্রথমে আমরা কোনো ফ্রেমওয়ার্কের সাহায্য না নিয়ে raw nodejs নিয়ে কাজ করি। + +```js +const http = require('http'); + +const server = http.createServer((req, res) => { + if (req.url === '/') { + res.write('

Hello World

'); + res.statusCode = 200; + res.end(); + } else if (req.url === '/hello') { + res.write('

Hello Guest

'); + res.statusCode = 200; + res.end(); + } else { + res.write('

404 not found!

'); + res.statusCode = 200; + res.end(); + } +}); + +server.listen(8000, () => { + console.log('Server is listening on port 8000'); +}); +``` + +প্রথমে আমরা nodejs থেকে http module ইমপোর্ট করে আনলাম। এরপর সার্ভার তৈরি করলাম http.createServer লিখে। এখানে একটা কলব্যাক ফাংশন থাকবে। এর দুইটা প্যারামিটার থাকবে। req and res. আমরা একটা কন্ডিশন লিখলাম। কন্ডিশনটা আশা করি বুঝতে পারছেন। যদি আমরা '/' এ হিট করি তাহলে আমাদের পেইজে শো করবে `Hello World`. যদি '/hello' তে হিট করি তাহলে আমাদের পেইজে শো করবে `Hello Guest`. আর যদি অন্য কোনো রাউটে হিট করি তাহলে শো করবে `404 not found!`। এরপর আমাদের সার্ভার লিসেন করতে হবে। আমরা পোর্ট `8000` দিয়ে দিবো লিসেন করার জন্য। + +এবার আমরা ব্যাকএন্ডের যে তিনটা কাজের কথা বলেছিলাম সেই তিনটা কাজ এখানে হয়েছে কিনা দেখি। এখানে আমাদের সার্ভার আমাদের রিকোয়েস্ট লিসেন করছে। সেটা প্রসেসও করছে, এবং সবশেষে রেসপন্স ব্যাক করছে। এখানে রিকোয়েস্ট লিসেন করা আর রেসপন্স ব্যাক করা সবসময় একই। কিন্তু প্রসেস করার ক্ষেত্রে আমাদের যতো ঝামেলা। চলুন আমরা একটা ওভারভিউ দেখি। + +- Listen Request + - Always Same +- Process + - Algorithm + - Data Structure + - Database + - Problem Solving + - CRUD +- Response + - Always Same + +এখানে এই কাজগুলো দেখেন বারবার বারবার লিখতে হচ্ছে। তাই আমরা এবার একটা ফ্রেমওয়ার্ক ইনস্টল করে নিবো যার নাম এক্সপ্রেস। + +এক্সপ্রেসে কিভাবে সার্ভার বানাতে হয় তা লেকচার ১৩ তে সুন্দরভাবে বলা হয়েছে। এখানে শুধু উদাহরণ দেখানো হবে। ডিটেইলসে আলোচনা ওখানে করা হয়েছে। আপনারা সেখান থেকে শিখতে পারবেন। + +```js +const express = require('express'); + +const app = express(); +app.use(express.json()); + +const books = [ + { + id: '1', + name: 'Personal Finance', + price: 500, + }, + { + id: '2', + name: 'Javascript for dummies', + price: 1000, + }, + { + id: '3', + name: 'JavaScript the definitive guide', + price: 1500, + }, + { + id: '4', + name: "You don't know js yet", + price: 2500, + }, + { + id: '5', + name: 'Atomic Habits', + price: 100, + }, + { + id: '6', + name: 'JavaScript the good parts', + price: 1200, + }, +]; + +app.get('/books', (req, res) => { + if (req.query.show === 'all') { + return res.json(books); + } + + if (req.query.price === '500') { + const result = books.filter((book) => book.price <= 500); + return res.json(result); + } + + if (req.query.price === '1000') { + const result = books.filter((book) => book.price <= 1000); + return res.json(result); + } + + return res.json(books); +}); + +app.post('/books', (req, res) => { + const book = req.body; + books.push(book); + + res.json(books); +}); + +app.listen(8000, () => { + console.log('Server is listening on port 8000'); +}); +``` + +কিছু কমন HTTP request method আছে। যেমনঃ + +- GET - want to read data from server +- POST - create new data +- PUT/PATCH - update existing content +- DELETE - delete data from database + +এবার একটু আমরা রাউটিং প্যাটার্ন দেখি, কোন কাজ করতে গেলে কিরকম রাউট হতে হবে। + +- GET Everything - /books +- Get one book - /books/bookId +- POST new book - /books +- Update Book - /books/bookId +- Delete Book - /book/booksId + +উপরের উদাহরণে books নামে একটা অ্যারে নেয়া হয়েছে। এই অ্যারেতে অনেকগুলো বইয়ের অবজেক্ট আছে যেখানে বইয়ের একটা আইডি দেয়া আছে, বইয়ের নাম দেয়া আছে আর দাম দেয়া আছে। এবার আমরা চাইছি '/books' এ হিট করলে সব বই শো করবে, '/books?show='all' এ হিট করলে সব বই শো করবে, '/books?price=500' এ হিট করলে ৫০০ টাকা পর্যন্ত দামের বইগুলো দেখাবে এবং '/books?price=1000' এ হিট করলে ১০০০ টাকা পর্যন্ত দামের বইগুলো দেখাবে। সেটা আমরা get মেথডের মাধ্যমে করলাম। এবার আমি চাইছি নতুন বই ঢুকাবো এই লিস্টে। তার জন্য আমাদের ইউজ করতে হবে post মেথড। আমরা রিকোয়েস্ট বডি থেকে যে অবজেক্ট পাবো তা অ্যারেতে পুশ করে দিবো। + +আমাদের একটু পোস্টম্যানে চেক করে দেখি আমাদের সার্ভার ঠিকঠাক কাজ করছে কিনা। + +আমরা প্রথমে আমাদের সার্ভার রান করে নিবো `yarn start` লিখে। start এর জায়গায় আপনি package.json এ যা দিবেন তা লিখবেন। + +নিচে পর্যায়ক্রমে '/books', '/books?show='all', '/books?price=500', '/books?price=1000' এর রেজাল্টের স্ন্যাপশট দেয়া হলো। + +![books](./images/books.png) +![all](./images/all.png) +![500](./images/500.png) +![1000](./images/1000.png) + +এবার আমাদের POST মেথড কাজ করে কিনা দেখি। নিচে তার স্ক্রিনশট দেয়া হলো। + +![postreq](./images/postreq.png) +![postres](./images/postres.png) + +আমাদের POST রিকোয়েস্টও সাক্সেসফুলি কাজ করছে। + +এবার আমরা আমাদের ব্যাকএন্ডের কাজগুলোকে একটা পাইপলাইন আকারে দেখাই। + +REQUEST -> MIDDLEWARE[logger, body parser, file parser, user ip, block ip, authentication, authorization, validation] -> CONTROLLER (Business Logic) -> MIDDLEWARE[error handler] -> RESPONSE + +মিডলওয়্যার, কন্ট্রোলার নিয়ে আমরা পরবর্তী ক্লাসগুলোতে আলোচনা করবো। + +পরবর্তী ক্লাস বুঝতে হলে এক্সপ্রেস সম্পর্কে বেসিক একটা ধারণা থাকা জরুরী। এই ধারণার জন্য স্ট্যাক লার্নার চ্যানেলে একটা ছোট প্লেলিস্ট আছে। যার নাম হচ্ছে [Express Js Crash Course In Bangla](https://youtube.com/playlist?list=PL_XxuZqN0xVDm9HkiP4h_76qNBZix6XME)। আপনারা এই প্লেলিস্ট শেষ করলে এক্সপ্রেস সম্পর্কে একটা বেসিক ধারণা পাবেন। সবাইকে এই প্লেলিস্ট শেষ করেই পরবর্তী লেকচার দেখার জন্য পরামর্শ দেয়া হচ্ছে। + +## Resource for this lecture + +এই লেকচারের সমস্ত রিসোর্স [লেকচার ১৫](../../resources/lecture-15/README.md) এ পাবেন। + +## Source Code + +এই লেকচারের সোর্স কোডসমূহ এই [লিংক](../../src/lecture-15/) এ পাবেন। + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/full-stack-army/class-overview/Lecture-15/images/1000.png b/full-stack-army/class-overview/Lecture-15/images/1000.png new file mode 100644 index 0000000..3f468ad Binary files /dev/null and b/full-stack-army/class-overview/Lecture-15/images/1000.png differ diff --git a/full-stack-army/class-overview/Lecture-15/images/500.png b/full-stack-army/class-overview/Lecture-15/images/500.png new file mode 100644 index 0000000..9f507be Binary files /dev/null and b/full-stack-army/class-overview/Lecture-15/images/500.png differ diff --git a/full-stack-army/class-overview/Lecture-15/images/all.png b/full-stack-army/class-overview/Lecture-15/images/all.png new file mode 100644 index 0000000..b279a10 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-15/images/all.png differ diff --git a/full-stack-army/class-overview/Lecture-15/images/books.png b/full-stack-army/class-overview/Lecture-15/images/books.png new file mode 100644 index 0000000..7126be0 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-15/images/books.png differ diff --git a/full-stack-army/class-overview/Lecture-15/images/postreq.png b/full-stack-army/class-overview/Lecture-15/images/postreq.png new file mode 100644 index 0000000..0475765 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-15/images/postreq.png differ diff --git a/full-stack-army/class-overview/Lecture-15/images/postres.png b/full-stack-army/class-overview/Lecture-15/images/postres.png new file mode 100644 index 0000000..e78241d Binary files /dev/null and b/full-stack-army/class-overview/Lecture-15/images/postres.png differ diff --git a/full-stack-army/class-overview/Lecture-16/README.md b/full-stack-army/class-overview/Lecture-16/README.md new file mode 100644 index 0000000..c2cc577 --- /dev/null +++ b/full-stack-army/class-overview/Lecture-16/README.md @@ -0,0 +1,602 @@ +# Lecture 16 - [Backend 3] Understand Express Middleware + +গত ক্লাসে আমরা একটা ব্যাকএন্ডের স্টেপের একটা পাইপলাইন বানিয়েছিলাম। যা ছিল এরকমঃ + +REQUEST -> MIDDLEWARE[logger, body parser, file parser, user ip, block ip, authentication, authorization, validation] -> CONTROLLER (Business Logic) -> MIDDLEWARE[error handler] -> RESPONSE + +প্রথমে আমরা রিকোয়েস্ট পাঠাচ্ছি। এরপর অনেক মিডলওয়্যার আমরা পার করছি। এরপর কন্ট্রোলার। এরপর আবার এরর হ্যান্ডলিং এর জন্য আরেকটা মিডলওয়্যার থাকলেও থাকতে পারে। সবশেষে রেসপন্স ব্যাক করা। এর মধ্যে আমরা রিকোয়েস্ট এবং রেসপন্সের কনসেপ্ট মোটামুটি ভালভাবে বুঝেছি। কিন্তু আমাদের এখনও সমস্যা আছে মিডলওয়্যার এবং কন্ট্রোলারে। যারা [Express Js Crash Course In Bangla](https://youtube.com/playlist?list=PL_XxuZqN0xVDm9HkiP4h_76qNBZix6XME) দেখেছেন তারা কিছুটা হয়তো বুঝেছেন। যারা প্লেলিস্টটা কমপ্লিট করেছেন তারা ধরতে গেলে ব্যাকএন্ডের ৭৫% শিখে ফেলেছেন। আর যদি ২৫% শিখে ফেলেন তাহলে দুনিয়াতে যতো অ্যাপ্লিকেশন আছে, তার ৯০% ব্যাকএন্ড আপনারা বানাতে পারবেন। + +আমরা এক্সপ্রেস, লারাভেল, জ্যাংগো, স্প্রিংবুট, ফ্লাস্ক, নেস্ট, রুবি অন রেইলস যেটা দিয়েই ব্যাকএন্ডের কাজ করি না কেন সব জায়গায় আমাদের এই পাইপলাইনটাই ব্যবহার করতে হবে। সুতরাং আমরা যদি এই পাইপলাইনটা ভালভাবে শিখে ফেলতে পারি তাহলে দুনিয়ার যেকোনো ব্যাকএন্ড ফেমওয়ার্ক দিয়ে কাজ করতে পারবো। কারণ আমাদের কনসেপ্ট জানা থাকবে। বাকিটা ডকুমেন্টেশন দেখে শিখে ফেলতে পারবো। এক্সপ্রেস যদি শিখতে পারি তাহলে অন্য ফ্রেমওয়ার্কে কাজ আরো সহজ। সেখানে কিছু কাজ করে রাখা হয়েছে। যেমন নেস্টে রিকোয়েস্ট, রেসপন্স অটোমেটিক্যালি করা আছে। সেখানে শুধু কাজ করতে হবে কন্ট্রোলার নিয়ে। এক্সপ্রেসে সব আমরা নিজে থেকে করবো। যদি আমাদের সব শেখা থাকে তাহলে অন্য যেকোনো ফেমওয়ার্কে আমরা সহজেই সুইচ করতে পারবো। + +ডাটাবেজের সাথে অ্যাপ্লিকেশনের কোনো সম্পর্ক নেই। আমরা যদি চাই আমাদের ভবিষ্যতে ব্যবহারের জন্য আমরা আমাদের ডাটাগুলোকে সংরক্ষণ করবো, সেক্ষেত্রে আমরা আমাদের অ্যাপ্লিকেশনে ডাটাবেজ কানেক্ট করবো। নাহয় আমাদের ডাটাবেজের প্রয়োজনই নেই। দুনিয়াতে এমন অনেক অ্যাপ্লিকেশন আছে যেখানে ডাটাবেজের প্রয়োজনই হয় না। + +আজ আমাদের আলোচ্য বিষয়গুলো নিচে দেয়া হলো। + +- Middleware +- Project Structure +- Project + +এই প্লেলিস্টে টেমপ্লেট ইঞ্জিন হিসেবে ejs ব্যবহার করা হয়েছিল। কিন্তু বর্তমানে প্রায় সব অ্যাপ্লিকেশন সিংগেল পেইজ হওয়ার কারণে আমাদের দরকার হয় রিয়্যাক্ট, ভিউ, অ্যাঙ্গুলার এর মতো ফ্রেমওয়ার্কগুলো। এখন আর ejs, handle bar, pug ইত্যাদি টেমপ্লেট ইঞ্জিনের দরকার হয় না। আমরা nodejs নিয়ে কাজ করছি মানে API বানাচ্ছি। তাই আমাদের মূল ফোকাস থাকবে API এর দিকে। আর ফ্রন্টএন্ডের জন্য তো আমাদের রিয়্যাক্ট, ভিউ এর মতো ফ্রেমওয়ার্ক আছেই। + +এখন আসি প্রথমে আমাদের মিডলওয়্যার জিনিসটা কি সেটা নিয়ে। + +- What is Middleware? + +এটা একটা ফাংশন। কি ধরণের ফাংশন? এটা একটা কন্ট্রোলার ফাংশন। আমরা গত ক্লাসে একটা ফাংশন লিখেছিলাম। + +```js +app.get('/books', (req, res) => { + if (req.query.show === 'all') { + return res.json(books); + } + + if (req.query.price === '500') { + const result = books.filter((book) => book.price <= 500); + return res.json(result); + } + + if (req.query.price === '1000') { + const result = books.filter((book) => book.price <= 1000); + return res.json(result); + } + + return res.json(books); +}); +``` + +এই ফাংশন আর মিডলওয়্যার ফাংশনের মধ্যে বেসিক কোনো পার্থক্য নেই। মিডলওয়্যার চাইলে যেকোনো জায়গা থেকে রেসপন্স পাঠিয়ে দিতে পারে। + +Req -> M1 -> M2 -> M3 -> Res + +এখানে M1 এর যে ক্ষমতা, M2 এরও একই ক্ষমতা, M3 এরও একই ক্ষমতা। আবার রেসপন্সেরও একই ক্ষমতা। যদি সবার ক্ষমতা এক হয় তাহলে এখানে ভিন্ন ভিন্ন মিডলওয়্যার কেন? ভিন্ন ভিন্ন হওয়ার কারণ তাদের রেসপন্সিবিলিটি। আমরা চাইলে এই তিনটি মিডলওয়্যার তৈরি না করে রেসপন্স থেকে এই কাজগুলো করে ফেলতে পারতাম। আমরা চাইলে প্রথম মিডলওয়্যার M1 দিয়েই রেসপন্স তৈরি করে ফেলতে পারতাম। তাহলে আমরা মিডলওয়্যার কেন বানাচ্ছি? মিডলওয়্যারের কনসেপ্ট এসেছে DRY (Don't repeat yourself) principle থেকে। আমাদের অনেক কাজ করতে হবে। প্রতিটা রিকোয়েস্টের জন্যই কাজগুলো সেইম। এখন তাহলে কি আমরা প্রতিটা রিকোয়েস্টের জন্য এই কোডগুলো বারবার লিখবো? নাকি একবার কোথাও লিখে সেটাকে বারবার যেখানে লাগবে সেখানে ব্যবহার করবো? অবশ্যই আমরা কোড রিইউজ করবো। এই রিইউজ কোডকে একটা ফাংশনের মধ্যে রাখা হয়। এই ফাংশনটাকেই বলে মিডলওয়্যার। আমি লিখবো একবার। কিন্তু চাইলে আমি সব রিকোয়েস্টে এটা ব্যবহার করবো, চাইলে কিছু কিছু রাউটে ব্যবহার করবো বাকিগুলোতে করবো না। বলতে গেলে কন্ট্রোল আমার হাতে। মিডলওয়্যার তাই এক ধরণের কন্ট্রোলার ফাংশন। + +একটা মিডলওয়্যার বানাতে গেলে এর একটা সিগ্নেচার আছে। সেটা একটু আমরা আগে দেখি। এরপর বিশ্লেষণ করবো। + +```js +// We will never call it, express will automatically invoke for us. +// This is middleware +function xyz(req, res, next) { + next(); +} + +// This is controller +function xyz(req, res, next) { + res.send(); +} +``` + +আমরা একটা ফাংশন নিলাম। এই ফাংশনটা আমরা কল করবো না। শুধু রেফারেন্স আকারে পাস করবো। আমাদের জন্য এই ফাংশনটাকে কল করবে এক্সপ্রেস। এখন এক্সপ্রেস এই ফাংশনটা কল করার সময় তিনটা প্যারামিটার দিবে। req, res, next. রিকোয়েস্ট আর রেসপন্স অবজেক্টের মধ্যে যতো মেথড আছে সব এক্সপ্রেস এখানের মধ্যে কল করতে পারে। next ফাংশন আমরা কল করে দিবো। যদি আমরা তা কল না করি তাহলে মিডলওয়্যার সমস্ত কাজ শেষে থেমে বসে থাকবে। অন্য কোথাও যেতে পারবে না। ধরেন উপরের পাইপলাইনে রিকোয়েস্ট পেলাম। এরপর সে দেখলো M1 আছে এরপর। সে তার সমস্ত কিছু M1 কে দিয়ে দিলো। এখন M1 রিকোয়েস্ট অবজেক্টকে প্রসেস করে দেখবে কি আছে এরপর। যদি next() লেখা দেখে তাহলে সে পরের মিডলওয়্যারের কাছে সব হ্যান্ডওভার করে দিবে। এখানে যদি আমরা next ফাংশন কল না করি তাহলে মিডলওয়্যার প্রসেসিং শেষে ওখানেই থেমে যাবে। কারণ আমরা বলে দিইনি তাকে কি করতে হবে এরপর। এখন এখানে একটা জিনিস বুঝার ব্যাপার আছে। আমরা বলছি মিডলওয়্যার একটা কন্ট্রোলার ফাংশন। কন্ট্রোলার ফাংশন কি? কন্ট্রোলার ফাংশন হলো সে সবশেষে রেসপন্স ব্যাক করে। সে আর কারো কাছে যাবে না। আর মিডলওয়্যার হলো যে সব শেষে next() ফাংশন কল করে অন্য কারো কাছে মডিফাইড ডাটা হ্যান্ডওভার করবে। + +আমরা গত ক্লাসে server.js এ `app.use(express.json())` লিখেছিলাম। express.json() এক ধরণের মিডলওয়্যার। মজার ব্যাপার হলো এক্সপ্রেসে যেভাবে রাউট লেখা হয় সেগুলোও এক ধরণের মিডলওয়্যার। সেগুলো আমরা পরে আলোচনা করবো। + +এবার একটু আমরা কোডে যায়। আমরা server.js নামে একটা ফাইল তৈরি করবো। এরপর যেভাবে এক্সপ্রেস ইমপোর্ট করে অ্যাপ বানাতে হয় বানাবো। + +```js +const express = require('express'); + +const app = express(); +``` + +এবার আমরা লিসেন করবো। + +```js +app.listen(8000, () => { + console.log('Application running on port 8000'); +}); +``` + +listen ফাংশন সাধারণত ফাইলের শেষে লেখা হয়। এটা একটা কনভেনশন। তাই আমরাও এটাকে সবার শেষে রাখবো। আর অন্যান্য কাজ এটার উপর করবো। + +এখন আমাদের কাজ কিভাবে মিডলওয়্যার বানাবো, কিভাবে রাউটার বানাবো সেগুলো নিয়ে। রাউটার বানানো অলরেডি আমরা শিখে ফেলেছি। আমরা প্রথমে রাউটার বানিয়ে ফেলি। + +```js +const express = require('express'); + +const app = express(); + +app.get('/hello', (req, res, next) => { + res.json({ message: 'Hello' }); +}); + +app.get('/', (req, res, next) => { + res.json({ message: 'Sweet Home' }); +}); + +app.listen(8000, () => { + console.log('Application running on port 8000'); +}); +``` + +এবার যদি আমরা সার্ভার রান করে ব্রাউজারে গিয়ে '/' এ হিট করি তাহলে {message: 'Sweet Home'} দেখাবে। আর যদি '/hello' তে হিট করি তাহলে { message: 'Hello' } দেখাবে। + +এখন প্রতিবার আমি হিট করলে ডাটা পাচ্ছি। কিন্তু আমি আমার কনসোলে সেটা বুঝতে পারছি না। আমি চাইছি প্রতিবার হিট করলে আমার কনসোলে তা লগ করবে। এটার জন্য আমরা আমাদের যে দুইটা রাউট আছে সেখানে আমরা `console.log(`${req.url} - ${req.method} - ${new Date().toISOString()}`);` এটা লিখে রাখতে পারি। req.url বলতে আমাদের রাউট, req.method মানে আমাদের রিকোয়েস্ট মেথড সেটা, এবং কোন সময় আমরা হিট করেছি সেটা আমরা আমাদের কনসোলে দেখতে চাই। এখন যদি আমরা ব্রাউজার '/' হিট করি তাহলে কনসোলে `/ - GET - 2022-06-26T07:43:17.067Z` এরকম আউটপুট আসবে। আর যদি '/hello' হিট করি তাহলে `/hello - GET - 2022-06-26T07:43:22.746Z` এরকম আউটপুট আসবে। + +এখন আমার তো ১০০০টা কন্ট্রোলার থাকতে পারে। আমি কি সবগুলোর জন্য এই লাইন লিখতেই থাকবো? মোটেই না। এখানেই আসবে আমাদের মিডলওয়্যার বানানোর উদ্দেশ্য। আমরা একটা মিডলওয়্যার বানিয়ে রাখবো। এরপর সব রাউট আমরা সেটা ইউজ করতে পারি। চলুন তাহলে আমরা একটা মিডলওয়্যার বানিয়ে ফেলি। + +```js +const express = require('express'); + +const app = express(); + +const simpleLogger = (req, res, next) => { + console.log(`${req.url} - ${req.method} - ${new Date().toISOString()}`); + next(); +}; + +app.get('/hello', (req, res, next) => { + res.json({ message: 'Hello' }); +}); + +app.get('/', (req, res, next) => { + res.json({ message: 'Sweet Home' }); +}); + +app.listen(8000, () => { + console.log('Application running on port 8000'); +}); +``` + +এবার আমরা এটাকে ব্যবহার করবো কিভাবে? দুইভাবে ব্যবহার করা যায়। গ্লোবালি এবং লোকালি। গ্লোবালি বলতে আমার যতো রিকোয়েস্ট আছে সবগুলোর জন্য আমি এটা ব্যবহার করবো, সেক্ষেত্রে আমরা গ্লোবালি ব্যবহার করবো। আর আমার সব রিকোয়েস্টে এটা লাগবে না, নির্দিষ্ট কিছু রিকোয়েস্টে লাগবে, সেক্ষেত্রে আমরা লোকালি ব্যবহার করবো। এখন ধরি আমরা শুধু আমার '/' রাউটের ক্ষেত্রে মিডলওয়্যার ব্যবহার করবো। তাহলে আমাদের লোকালি ব্যবহার করতে হবে। + +```js +const express = require('express'); + +const app = express(); + +const simpleLogger = (req, res, next) => { + console.log(`${req.url} - ${req.method} - ${new Date().toISOString()}`); + next(); +}; + +app.get('/hello', (req, res, next) => { + res.json({ message: 'Hello' }); +}); + +app.get('/', simpleLogger, (req, res, next) => { + res.json({ message: 'Sweet Home' }); +}); + +app.listen(8000, () => { + console.log('Application running on port 8000'); +}); +``` + +এখানে আমরা মিডলওয়্যারটা আমাদের কন্ট্রোলার ফাংশনের আগে বসাবো। আমরা কল করবো না, আমাদের হইয়ে এক্সপ্রেস কল করবে, আমরা শুধু আমাদের ফাংশনের রেফারেন্সটা পাঠিয়ে দিবো। আমরা চাইলে যত ইচ্ছা মিডলওয়্যার পাশাপাশি বসিয়ে যেতে পারি। কোনো সমস্যা নেই। + +এবার আমরা চাইছি আমরা সব রাউটের জন্য এই মিডলওয়্যারটা ব্যবহার করবো। তাহলে কি সব রাউটের ভিতর আমার লিখতে হবে? কোনো প্রয়োজন নেই। আমরা গ্লোবালি সেটা একবারেই করে দিতে পারি জাস্ট রাউটের আগে `app.use(simpleLogger);` লিখে। + +```js +const express = require('express'); + +const app = express(); + +const simpleLogger = (req, res, next) => { + console.log(`${req.url} - ${req.method} - ${new Date().toISOString()}`); + next(); +}; + +app.use(simpleLogger); + +app.get('/hello', (req, res, next) => { + res.json({ message: 'Hello' }); +}); + +app.get('/', (req, res, next) => { + res.json({ message: 'Sweet Home' }); +}); + +app.listen(8000, () => { + console.log('Application running on port 8000'); +}); +``` + +এবার সব রাউটের জন্যই এটা কাজ করবে। আপনারা চেক করে দেখতে পারবেন। + +যদি আমরা দুইটা মিডলওয়্যার গ্লোবালি ব্যবহার করতে চাই তাহলেও পারবো। তখন মিডলওয়্যারের একটা অ্যারে আকারে রাখতে হবে। যেমনঃ + +```js +const express = require('express'); + +const app = express(); + +app.use(express.static(__dirname + '/public')); + +const simpleLogger = (req, res, next) => { + console.log(`${req.url} - ${req.method} - ${new Date().toISOString()}`); + next(); +}; + +const secondMiddleWare = (res, req, next) => { + console.log('I am second middleware'); + next(); +}; + +app.use([simpleLogger, secondMiddleWare]); + +app.get('/hello', (req, res, next) => { + res.json({ message: 'Hello' }); +}); + +app.get('/', (req, res, next) => { + res.json({ message: 'Sweet Home' }); +}); + +app.listen(8000, () => { + console.log('Application running on port 8000'); +}); +``` + +এখন যদি আমরা হিট করি আমরা কনসোলে দুইটা মিডলওয়্যারের আউটপুটই দেখতে পারবো। + +এবার আমরা মিডলওয়্যারের ইউজ কেইসগুলো বুঝার চেষ্টা করি। তার জন্য আমরা এক্সপ্রেস মিডলওয়্যারের [ডকুমেন্টেশন](https://expressjs.com/en/resources/middleware.html) এ যেতে হবে। এখানে কিছু built-in মিডলওয়্যার আছে, এগুলো নিয়ে একটু আলোচনা করা যাক। + +- body-parser: এটা মূলত ক্লায়েন্ট থেকে সার্ভারে ডাটা পাঠাতে ব্যবহার করা হয়। সেটা হতে পারে ইমেজ, ফাইল বা একটা সিংগেল আর্গুমেন্ট। +- compression: আমাদের রেসপন্সের সাইজ যখন অনেক বেশি হয়ে যাবে তখন টাইম অনেক বেশি লাগবে। আর এই রেসপন্সকে কমপ্রেস করে ছোট করার জন্য আমরা এই মিডলওয়্যার ব্যবহার করে থাকি। +- connect-rid: আমরা যখন মাইক্রোসার্ভিস নিয়ে কাজ করি তখন একটা রিকোয়েস্ট মাল্টিপল সার্ভার ঘুরে বেড়ায়। যেমন আপনি চাইছেন আপনার একটা ইমেজ লাগবে। ক্লায়েন্ট থেকে অ্যাপ্লিকেশন রিকোয়েস্ট করলো যে আমার একটা ইমেজ লাগবে। এখন সেই ইমেজটা পাওয়ার জন্য প্রথমে আপনার অথোরাইজেশন সার্ভারে রিকোয়েস্টটা যাবে, অথেনটিকেশন চেক করবে, আপনার পারমিশন আছে কিনা চেক করবে, এরপর খুঁজে বের করবে এই ইমেজটা কোথায় রাখা আছে, তারপর সেখান থেকে যাবে কোনো স্টোরেজ সার্ভারে, সেখান থেকে রাউটটাকে কল করে ইমেজটাকে খুঁজে নিয়ে আসবে। তার মানে মাল্টিপল সার্ভারে কমিউনিকেশন হবে। কিন্তু মাল্টিপল সার্ভারে কমিউনিকেশন করলেও এটা মেইনলি একটা রিকোয়েস্টের জন্য কাজ করবে। কারণ আপনি ক্লায়েন্ট থেকে একটা রিকোয়েস্টই জেনারেট করেছেন। তো এই ক্ষেত্রে রিকোয়েস্ট আইডি অনেক কাজ দেয়। একটা রিকোয়েস্ট আইডি দিয়ে আমরা ভেরিফাই করতে পারি কোন রিকোয়েস্টের জন্য আমরা বাকি রিকোয়েস্টগুলো জেনারেট করেছি। ফিল্টার করার কাজে রিকোয়েস্ট আইডি লাগে। আর এই রিকোয়েস্ট আইডি জেনারেট করার জন্য আমরা এই মিডলওয়্যারটা ব্যবহার করি। +- cookie-parser: যদি আমাদের কোনো রিকোয়েস্টের হেডারের মধ্যে কুকি পাওয়া যায়, সেটা স্ট্রিং আকারে থাকে। এই মিডলওয়্যার দিয়ে আমরা কুকি পার্স করে অবজেক্ট আকারে req.cookies এর মধ্যে রাখতে পারি। +- cookie-session: যখন আমরা কুকি নিয়ে কাজ করি তখন আমাদের সেশন নিয়েও কাজ করতে হয়। সেই সেশন নিয়ে কাজ করার জন্যই আমাদের এই মিডলওয়্যার। +- CORS (Cross Origin Resource Sharing): ব্রাউজারের একটা বিহেভিয়ার হলো যখন ক্লায়েন্ট এবং সার্ভারের ডোমেইন একই না হয় তখন ক্লায়েন্টের সমস্ত রিকোয়েস্ট সার্ভার ব্লক করে দেয়। কারণ দুইটা ভিন্ন সার্ভার হলে ক্রস সার্ভার হয়ে গেলো। তখন সার্ভার ধরে নেয় এখানে হ্যাকিং বা অন্য কোনো সমস্যা হতে যাচ্ছে। তাই সে রিকোয়েস্ট ব্লক করে দেয়। এই মিডলওয়্যার ব্যবহার করে আমরা নির্দিষ্ট করে দিতে পারি যে কোন কোন ডোমেইনকে আমরা আমাদের অ্যাপ্লিকেশনে অ্যালাউ করছি। +- csrf: ধরেন আপনি একটা ফর্ম সাবমিট করছেন। এই ফর্মের ডাটাকে ম্যানিপুলেট করা যায়। এখন এই মিডলওয়্যার ইউজ করে যখন ফর্ম রেন্ডার করা হয় তখন সাবমিটেড ফর্মের আইডি আর রেন্ডারকৃত ফর্মের আইডি মিলিয়ে দেখা হয়। এটা মাল্টিপেইজ অ্যাপ্লিকেশনের ক্ষেত্রে করতে হয়, সিংগেল পেইজের অ্যাপ্লিকেশনের ক্ষেত্রে করতে হয় না। +- errorHandler: আমরা সাধারণত ম্যানুয়েলি প্রতিটা রাউটের জন্য এরর হ্যান্ডলিং করে থাকি। তবে কিছু এরর আছে যেগুলো গ্লোবাল। ঐ গ্লোবাল এরর হ্যান্ডেল করার জন্য এই মিডলওয়্যার ব্যবহার করা হয়। +- method-override: এই মিডলওয়্যার ব্যবহার করে আমরা এক মেথডকে অন্য মেথডে কনভার্ট করতে পারি। যেমন আমরা GET মেথডকে POST মেথডে কনভার্ট করে ফেলতে পারি। +- morgan: আমরা যে লগার তৈরি করেছিলাম তার মধ্যে কোনো ফাংশনালিটিজই নেই। সেগুলো আমাদের তৈরি করারও কোনো প্রয়োজন নেই। আমরা সেগুলো এই মিডলওয়্যার ব্যবহার করে করতে পারবো। +- multer: কোন ফর্ম থেকে ডাটা বের করে এনে কোনো একটা ফাইলে সেইভ করে রাখার কাজটা করে multer। +- response-time: এই মিডলওয়্যার ব্যবহার করে আমরা একটা রেসপন্স জেনারেট হতে কতো সময় লাগে সেটা বের করতে পারি। রেস্পন্স টাইমের মাধ্যমে আমরা বুঝতে পারি আমাদের কোন কন্ট্রোলারকে আরো অপটিমাইজ করতে হবে। মূলত পারফরম্যান্স অপটিমাইজেশনের কাজ করার জন্য এই মিডলওয়্যার ব্যবহার করা হয়। +- serve-favicon: আমরা যখন রিকোয়েস্ট বা রেসপন্স নিয়ে কাজ করি তখন স্বাভাবিকভাবেই favicon গুলো যায় না। যদি আলাদাভাবে favicon গুলো পাঠাতে হয় সেক্ষেত্রে আমরা এই মিডলওয়্যার ব্যবহার করবো। +- serve-index: Index ফাইল সার্ভ করার জন্য। +- serve-static: আমরা যদি কোনো ফাইল পাবলিকলি অ্যাভেইলেবল করতে চাই তাহলে এই মিডলওয়্যার ব্যবহার করতে পারি। ধরি আমাদের প্রজেক্ট ডিরেক্টরিতে আমরা public নামে একটা ডিরেক্টরি ক্রিয়েট করে এতে index.html ফাইল ক্রিয়েট করেছি। + +```html + + + + + + + Hello NodeJS + + +

I am from public directory

+ + +``` + +আমি চাইছি ইউজার এই ফাইল দেখবে। কেউ এসে আমাদের এড্রেস লিখে /index.html লিখে এন্টার দিলেই এই ফাইলের মধ্যে যা আছে তা শো করবে। সেটা করার জন্য আমাদের server.js এ এটা পাবলিক করে দিতে হবে `app.use(express.static(__dirname + '/public'));` লিখে। + +```js +// Demo Code +const express = require('express'); + +const app = express(); + +app.use(express.static(__dirname + '/public')); + +app.listen(8000, () => { + console.log('Application running on port 8000'); +}); +``` + +এখানে যে আপনাকে public নামই দিতে হবে কথা নেই। আপনি যে নাম দিবেন সেই নামেই কনফিগার করবেন। + +- timeout: ধরেন আমার ইউজার আমাকে রিকোয়েস্ট দিচ্ছে, কিন্তু সেই ডাটাটা আছে আবার অন্য সার্ভারের কাছে, সে আমাকে রেসপন্স দিচ্ছে না। এই অবস্থায় আমি ১০ সেকেন্ড অপেক্ষা করার পর আমার ইউজারকে একটা ম্যাসেজ দিতে পারি যে ডাটাটা এখন পাওয়া যাচ্ছে না, আমরা বিষয়টি দেখছি। এটা এই timeout এর মাধ্যমে করা যায়। + +এগুলো গেলো বিল্টইন মিডলওয়্যারের ইউজ কেইস। আরো কিছু ইউজ কেইস আছে রিয়েলটাইম। ধরেন আপনি একটা ফর্ম বানালেন। এখন ইউজার যা খুশি তা ইনপুট দিতে পারে। সে ছবি জায়গায় ভিডিও আপলোড করতে পারে, নামের জায়গায় বয়স লিখতে পারে, ইমেইলের জায়গায় নাম লিখতে পারে। এর উপর আমাদের কন্ট্রোল নেই। আমরা কি করতে পারি? আমরা কিছু Worst case ভেবে আমাদের ভ্যালিডেশন চেক করার জন্য মিডলওয়্যার তৈরি করতে পারি। যে এখানে যা দেয়ার তুমি তাইই দিবে, নাহয় আমি সেটা নিবো না। এটা ইউজারকে ম্যাসেজ দিতে হবে। আমি আমার কন্ট্রোলারে আসার আগেই সব চেক করে শুধু ঠিক জিনিসকেই আসতে দিবো। ভুলভাল কিছু আমার মেইন কন্ট্রোলারের কাছে আমি আসতে দিবো না। এটাই মিডলওয়্যারের কনসেপ্ট। + +আবার ধরেন আমাদের অ্যাপ্লিকেশনে সেই লাইক, কমেন্ট করতে পারবে যে লগইন করবে। এখন ইউজার লগড ইন কিনা তা কন্ট্রোলারে আসার আগে চেক করবে মিডলওয়্যার। যদি লগডইন হয় তাহলে সে কন্ট্রোলারে যেতে পারবে, নাহয় না। + +এবার পুরো ব্যাকএন্ড ডেভেলপমেন্টকে এক জায়গায় নিয়ে আসি। ইউজার একটা রিকোয়েস্ট দিবে। সেটা একটা মিডলওয়্যারের কাছে যাবে। সেখানে তা ভ্যালিড হলে পরবর্তী মিডলওয়্যারের কাছে যাবে। এভাবে করতে করতে যখন বুঝবে যে এটা একটা ভ্যালিড রিকোয়েস্ট তখনই তা মেইন কন্ট্রোলারের কাছে যাবে। নাহয় যেখানেই ভ্যালিডেশন ফেইল করবে সেখানেই রিকোয়েস্ট শেষ হয়ে যাবে। এটাই ব্যাকএন্ড ডেভেলপমেন্টের কন্সেপ্ট। + +এক্সপ্রেসের কনসেপ্ট এখানেই শেষ। এক্সপ্রেসে আর বুঝার তেমন কিছু নেই। এখন যেটা বাকি আছে সেটা হলো প্রজেক্ট স্ট্রাকচার করা আর প্রজেক্ট সেটাপ করা। + +## Project Structure + +একটা প্রজেক্টের ফাইল স্ট্রাকচার করা অনেক কঠিন একটা কাজ। কিভাবে আমরা আমাদের প্রজেক্টের ফাইল স্ট্রাকচার করবো। এর জন্য একটা ডিজাইন প্রিন্সিপাল আছে। এর নাম [Clean Code Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)। এখানে কিভাবে ফাইল স্ট্রাকচার করতে হয় সে বিষয়ে আলোচনা করা হয়েছে। একটা প্রজেক্টের ফাইল স্ট্রাকচার যদি ঠিক না হয় তাহলে আপনি প্রজেক্ট করতে গিয়ে বারবার সমস্যার সম্মুখীন হবেন। যদিও এখনই আমরা ক্লিন কোড আর্কিটেকচারের দিকে ঝুঁকবো না। এটা একটা অ্যাডভান্সড কনসেপ্ট। আমরা পরে এটা নিয়ে আলোচনা করবো। এটা একটা Monolithic application এর জন্য বেস্ট একটা কনসেপ্ট। আমরা এখন যে অ্যাপ্লিকেশন বানাচ্ছি সেটা মনোলিথিক। কারণ সবকিছু এক জায়গায়ই আছে। এই মনোলিথিক আর্কিটেকচারের আভিধানিক নাম Layred architecture. তবে এটা মনোলিথিক আর্কিটেকচার নামেই পরিচিত। + +মনোলিথিক অ্যাপ্লিকেশনের জন্য আপাতত কোন প্রজেক্ট স্ট্রাকচার বেস্ট হবে সেটা বলা হচ্ছে না। আগে আমাদের যা যা লাগবে সবকিছুর জন্য আলাদা আলাদা ফোল্ডার বা ফাইল বানিয়ে চলে যাবো। কি কি ফোল্ডার / ফাইল বানাতে হবে এবং কেন চলুন আমরা একটু আলোচনা করি। + +1. app: এই ফোল্ডারে আমরা আমাদের অ্যাপ্লিকেশন রিলেটেড এরর, ডাটাবেজ কানেকশন, অ্যাপ ফাইল, রাউটস (গ্লোবাল, all) এগুলো রাখবো। server.js এর সাথে এই ফোল্ডারের কোনো সম্পর্ক নেই। +2. routes: আমরা আমাদের সকল রাউট এর মধ্যে রাখবো। +3. models: আমরা আমাদের সকল ডাটা মডেল এখানে রাখবো। +4. controller: যেখানে রাউট থাকবে সেখানেই কন্ট্রোলার থাকবে। সকল কন্ট্রোলার থাকবে আমাদের এই ফোল্ডারে। +5. service: আমরা কখনই আমাদের কন্ট্রোলারকে ডাটাবেইজের সাথে কমিউনিকেশন করতে দিবো না। এজন্য আমরা এই ডিরেক্টরি ক্রিয়েট করেছি ডাটাবেইজের সাথে কমিউনিকেট করার জন্য। যদিও কন্ট্রোলারও কমিউনিকেশন করতে পারে। কিন্তু আমরা ডাটাবেজ কানেকশন লজিক আর বিজনেস লজিক আলাদাভাবে লিখবো ভালভাবে বুঝার জন্য। +6. middleware: এখানে আমরা আমাদের কাস্টম মিডলওয়্যারগুলো লিখবো। +7. util: আমাদের যদি কোন ধরণের ইউটিলিটি ফাইল লাগে তা আমরা এই ফোল্ডারে রাখবো। +8. db: সমস্ত ডাটাবেজ সম্পর্কিত কাজ আমরা এখানে রাখবো। +9. config: এটা আমরা কনফিগারেশন ম্যানেজ করার জন্য ব্যবহার করবো। +10. log: সমস্ত অ্যাপ্লিকেশন লগ এখানে থাকবে। +11. error: আমরা আমাদের সমস্ত কাস্টম এরর এখানে লিখবো। +12. test: আমাদের অ্যাপ্লিকেশনকে টেস্ট করার কোড আমরা এখানে লিখবো। +13. server.js: সকল সার্ভার সম্পর্কিত কোড এই ফোল্ডারে থাকবে। +14. .env & default.env: সকল সিক্রেট ইনফরমেশন যা শুধু আমিই জানবো, আর কাউকে জানতে দিবো না সেগুলো এই ফাইলের মধ্যে থাকবে। + +app ফোল্ডারের মধ্যে আমরা app.js নামে একটা ফাইল নিবো। সেখানে আগের মতোই আমাদের অ্যাপ্লিকেশন বানাবো। এখন একটা রাউট সমস্ত অ্যাপ্লিকেশনের API তে থাকতেই হয় যেটা আমরা জানি না এবং বেশির ভাগ ক্ষেত্রে ব্যবহারই করিনা। কিন্তু এটা না থাকলে আমাদের API কে অনেক জায়গায় পারফেক্ট API বলে ধরাই হবে না। এটার কোনো কাজ নাই, কিন্তু এটা থাকতেই হয়। সেটা হলো '/health' রাউট। এটা সাধারণত যখন আমরা kubernetes cluster নিয়ে কাজ করবো, বা থার্ড পার্টি সার্ভিস ব্যবহার করে ডেপ্লয় করবো তখন তারা সাধারণত API চেক করার জন্য '/health' এ একটা রিকোয়েস্ট পাঠায়। যদি তা 200 রিটার্ন না করে তাহলে তারা ধরে নেয় এই API পারফেক্ট না। + +```js +const express = require('express'); + +const app = express(); + +app.get('/health', (_req, res) => { + res.status(200).json({ message: 'Success' }); +}); + +module.exports = app; +``` + +এখানে দেখুন আমরা আমাদের req প্যারামিটার ব্যবহার করিনি। এরকম আনইউজড ভ্যারিয়েবল থাকলে তার আগে একটা আন্ডারস্কোর (\_) বসিয়ে দিবেন। নাহয় কিছু কিছু প্ল্যাটফর্মে তা এরর থ্রো করবে। + +আমরা এতক্ষণ পর্যন্ত যত কাজ করেছি সবজায়গায় রাউটের পরে লিসেন করে দিয়েছি। কিন্তু এখন থেকে তা আর করবো না। আমরা app কে এক্সপোর্ট করে দিবো। এর কারন হলো আমরা যে একটা অ্যাপ্লিকেশন তৈরি করলাম আমাদের সেটা টেস্ট করতে হবে। এখন তার জন্য দরকার আমার অ্যাপ্লিকেশনের চেহারা। রানিং অ্যাপ্লিকেশন না। যদি এখানে আমরা লিসেন করতাম তাহলে তো কাজ শেষ। আমরা আর আমাদের অ্যাপ্লিকেশনটাকে পেতাম না। আর না পেলে কিভাবে টেস্ট করতাম? তাই আমরা এখানে শুধু অ্যাপ্লিকেশনের চেহারা রেখেছি। রান করার অপশন এখানে রাখবো না। আমাদের যখন টেস্ট করার দরকার পড়বে আমরা পরে আমাদের টেস্ট ফোল্ডারে ইমপোর্ট করে টেস্ট করতে পারবো। আমরা চাইলে সার্ভারেও ইমপোর্ট করতে পারবো। এবার তাহলে আমাদের সার্ভারটা কেমন হবে? + +```js +// server.js + +const http = require('http'); +const app = require('./app/app'); + +const server = http.createServer(app); + +const PORT = 8000; + +server.listen(PORT, () => { + console.log(`Server is listening on PORT ${PORT}`); +}); +``` + +এখন এখানে আমরা স্ট্যাটিকভাবে দিয়ে দিয়েছি যে 8000 পোর্টে এটা রান হবে। কিন্তু ডেপ্লয় করার সময় যদি পোর্ট চেইঞ্জ হয়ে যায় তখন আমরা কি করবো? এজন্য আমরা একটা কাজ করতে পারি। যখন একটা সার্ভার ডেপ্লয় হয় তখন কিছু সেনসিটিভ ডাটা আছে যা আমরা আমাদের কোডের মধ্যে রাখতে পারি না। আমরা সেগুলো এক্সপোজ করতে দিতে পারি না। সেগুলো process.env ফাইল থেকে আমাদের অ্যাপ্লিকেশনে পাঠানো হয়। এখানে আমরা dotenv নামে একটা প্যাকেজ ইনস্টল করে নিবো। নেয়ার পর আমরা .env ফাইল নামে একটা ফাইল ক্রিয়েট করবো। এবং সেখানে আমরা আমাদের যে ডাটাগুলো এনভায়রনমেন্ট ভ্যারিয়েবলে থাকে সেগুলো রাখবো। যখন আমরা সার্ভারে ডেপ্লয় করি তখন হয় এই এনভায়রনমেন্ট ভ্যারিয়েবলগুলো সেখানে বলে দিই, নাহয় সেখানে একটা .env ফাইল ওপেন করি। এখন আমাদের ডিরেক্টরিতে দুইটা ফাইল ক্রিয়েট করতে হবে। .env এবং default.env। default.env তে আমরা আমাদের .env ফাইলের ভ্যারিয়েবলগুলোই দিবো ডামি ডাটা হিসেবে যাতে কেউ যদি আমার কোডের এক্সেস পেয়েও যায় সে যেন আমার ডাটাগুলো না পায়। এবার .env ফাইলের মধ্যে আমরা নিচের ভ্যারিয়েবল স্টোর করে রাখবো। + +```env +PORT = 4444 +``` + +এবার server.js এ আমরা সরাসরি পোর্ট না লিখে এই পোর্টটা ওখানে নিয়ে ব্যবহার করবো। আমরা আগে চেক করবো process.env.PORT আছে কিনা? যদি না থাকে তাহলে আমরা 8000 দিয়ে দিবো। কোড দেখলে আরো ভালভাবে বুঝবেন। + +```js +// server.js +require('dotenv').config(); +const http = require('http'); +const app = require('./app/app'); + +const server = http.createServer(app); + +const PORT = process.env.PORT || 8000; + +server.listen(PORT, () => { + console.log(`Server is listening on PORT ${PORT}`); +}); +``` + +আমরা app.js এ গিয়েও উপরে এনভায়রনমেন্ট ভ্যারিয়েবলটা রিকোয়ার করে দিবো। + +```js +// app.js + +require('dotenv').config('../.env'); +const express = require('express'); +const app = express(); + +app.get('/health', (_req, res) => { + res.status(200).json({ message: 'Success' }); +}); + +module.exports = app; +``` + +app.js আমাদের এন্ট্রি ফাইল। এই ফাইলে অনেকে দেখা যায় এক হাজার লাইন লিখে ফেলে। যেটা আমার এন্ট্রি ফাইল সেটাতে যদি আমি এক হাজার লাইন লিখে ফেলি তাহলে সেটা মেইনটেইন করা অনেক দুরূহ হয়ে পড়ে। তাই আমাদের উদ্দেশ্য থাকবে এই ফাইলকে সর্বোচ্চ লেভেলে ক্লিয়ার রাখতে। আগে আমরা সব লিখি এরপর কিভাবে ক্লিয়ার রাখতে হবে সেটা দেখাবো। আমরা প্রথমে cors আর morgan ইনস্টল করি। এরপর আমরা সেগুলো app.js এ ইমপোর্ট করে নিই। + +```js +// app.js + +require('dotenv').config('../.env'); +const express = require('express'); +const app = express(); +const morgan = require('morgan'); +const cors = require('cors'); + +const middleware = [morgan('dev'), cors(), express.json()]; + +app.get('/health', (_req, res) => { + throw new Error('Error'); + res.status(200).json({ message: 'Success' }); +}); + +module.exports = app; +``` + +ধরি যদি কোনো কারণে /health একটা এরর থ্রো করলো তাহলে তার চেহারা হবে এমন। + +![error](./images/error.png) + +এখন আমি কি ক্লায়েন্টকে এই এরর শো করবো? কখনোই না। ম্যানুয়েলি যেখানে এরর তৈরি হবে সেখানেই আমরা ফরম্যাটেড এরর ম্যাসেজ শো করবো। আমরা 404 এর জন্য একটা এরর তৈরি করবো আর একটা গ্লোবাল এরর তৈরি করি। + +```js +// app.js + +require('dotenv').config('../.env'); +const express = require('express'); +const app = express(); +const morgan = require('morgan'); +const cors = require('cors'); + +const middleware = [morgan('dev'), cors(), express.json()]; + +app.get('/health', (_req, res) => { + throw new Error('Error'); + res.status(200).json({ message: 'Success' }); +}); + +app.use((_req, _res, next) => { + const error = new Error('Resource not found'); + error.status = 404; + next(error); +}); + +app.use((error, _req, res, _next) => { + if (error.status) { + res.status(error.status).json({ + message: error.message, + }); + } + + res.status(500).json({ message: 'Something went wrong' }); +}); + +module.exports = app; +``` + +প্রথমটা শুধু 404 এরর হ্যান্ডেল করবে। যদি কোন রাউট না পায় তাহলে এটা রিটার্ন করবে। আর যদি অন্য কোনো এরর হয় সেটা আমাদের গ্লোবাল এরর হ্যান্ডলার হ্যান্ডেল করবে। + +এবার আমরা কোডগুলোকে ডিস্ট্রিবিউট করে ফেলবো আমাদের সুবিধার্থে। না করলেও কিছু যায় আসবে না। কিন্তু যদি কয়েক হাজার লাইন কোড হয়ে সেগুলো ম্যানেজ করতে আমাদের হিমশিম খেয়ে যেতে হবে। তাই আমরা এখন এই কোডগুলোকে ডিস্ট্রিবিউট করবো। + +প্রথমে আমাদের মিডলওয়্যার আছে ৩টা। ভবিষ্যতে তো আরো থাকতে পারে। এতগুলো মিডলওয়্যার এখানে থাকলে ফাইলটা অনেক ভারী হয়ে যাবে। সুতরাং আমরা প্রথমে middleware.js নামে একটা ফাইল ক্রিয়েট করবো app ফোল্ডারের ভিতরেই। এবং app.js থেকে মিডলওয়্যারের কোডগুলোকে ওখানে নিয়ে যাবো। এবং পরে তা app.js এ ইমপোর্ট করে আনবো। + +```js +// middleware.js + +const express = require('express'); +const morgan = require('morgan'); +const cors = require('cors'); + +const middleware = [morgan('dev'), cors(), express.json()]; + +module.exports = middleware; +``` + +```js +// app.js + +require('dotenv').config('../.env'); +const express = require('express'); +const app = express(); + +app.use(require('./middleware')); + +app.get('/health', (_req, res) => { + res.status(200).json({ message: 'Success' }); +}); + +app.use((_req, _res, next) => { + const error = new Error('Resource not found'); + error.status = 404; + next(error); +}); + +app.use((error, _req, res, _next) => { + if (error.status) { + res.status(error.status).json({ + message: error.message, + }); + } + + res.status(500).json({ message: 'Something went wrong' }); +}); + +module.exports = app; +``` + +এবার আমরা আমাদের রাউটগুলোকে আলাদা করবো। যদিও এখানে এখন আছে একটি রাউট। সেটা আমরা routes.js নামে একটা ফাইল ক্রিয়েট করে সেখানে রাখবো। + +```js +// routes.js + +const router = require('express').Router(); + +router.get('/health', (_req, res) => { + res.status(200).json({ message: 'Success' }); +}); + +module.exports = router; +``` + +```js +// app.js + +require('dotenv').config('../.env'); +const express = require('express'); + +const app = express(); + +app.use(require('./middleware')); +app.use(require('./routes')); + +app.use((_req, _res, next) => { + const error = new Error('Resource not found'); + error.status = 404; + next(error); +}); + +app.use((error, _req, res, _next) => { + if (error.status) { + res.status(error.status).json({ + message: error.message, + }); + } + + res.status(500).json({ message: 'Something went wrong' }); +}); + +module.exports = app; +``` + +এবার আমরা ক্রিয়েট করবো error.js নামে একটা ফাইল। এবং সেখানে আমরা এরর হ্যান্ডলারগুলো রাখবো। + +```js +// error.js + +const notFoundHandler = (_req, _res, next) => { + const error = new Error('Resource not found'); + error.status = 404; + next(error); +}; + +const errorHandler = (error, _req, res, _next) => { + if (error.status) { + res.status(error.status).json({ + message: error.message, + }); + } + + res.status(500).json({ message: 'Something went wrong' }); +}; + +module.exports = { + notFoundHandler, + errorHandler, +}; +``` + +```js +// app.js + +require('dotenv').config('../.env'); +const express = require('express'); +const { errorHandler, notFoundHandler } = require('./error'); + +const app = express(); + +app.use(require('./middleware')); +app.use(require('./routes')); + +app.use(notFoundHandler); + +app.use(errorHandler); + +module.exports = app; +``` + +এখন দেখুন ফাইলটা কত ক্লিন লাগছে। যদি কোনো কিছু ম্যানেজ করতে হয় আমরা ফাইলে গিয়ে গিয়ে ম্যানেজ করতে পারবো। এভাবে যদি আমরা ছোট ছোট ফাইলে সব ভাগ করে নিই তাহলে আমাদের জন্য কাজ করা অনেক সহজ হয়ে যাবে। + +## Single Page vs Multi Page Application + +সিংগেল পেইজ অ্যাপ্লিকেশন বলতে বুঝায় যে অ্যাপ্লিকেশনের মাত্র একটা এইচটিএমএল ফাইল থাকবে, ব্যাকএন্ডে কোনো এইচটিএমএল জেনারেট হবে না, ব্যাকএন্ডের সাথে কমিউনিকেশন হবে শুধু API দ্বারা, আর সমস্ত পেইজ জেনারেট হবে ফ্রন্টএন্ডে ডায়নামিক্যালি জাভাস্ক্রিপ্ট দ্বারা। আর মাল্টিপেইজ অ্যাপ্লিকেশন বলতে বুঝায় যেখানে ব্যাকএন্ডেই html জেনারেট হয় যেমন গত ক্লাসের প্লেলিস্টে যেভাবে আমরা করেছিলাম সেটা মাল্টিপেইজ অ্যাপ্লিকেশন। + +## Resource for this lecture + +এই লেকচারের সমস্ত রিসোর্স [লেকচার ১৬](../../resources/lecture-16/README.md) এ পাবেন। + +## Source Code + +এই লেকচারের সোর্স কোডসমূহ এই [লিংক](../../src/lecture-16/) এ পাবেন। + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/full-stack-army/class-overview/Lecture-16/images/error.png b/full-stack-army/class-overview/Lecture-16/images/error.png new file mode 100644 index 0000000..94bbb0d Binary files /dev/null and b/full-stack-army/class-overview/Lecture-16/images/error.png differ diff --git a/full-stack-army/class-overview/Lecture-17/README.md b/full-stack-army/class-overview/Lecture-17/README.md new file mode 100644 index 0000000..b99d652 --- /dev/null +++ b/full-stack-army/class-overview/Lecture-17/README.md @@ -0,0 +1,968 @@ +# Lecture 17 - Backend 4 | Raffle Draw Project + +আমরা গত ক্লাসে এক্সপ্রেস, মিডলওয়্যার, কন্ট্রোলার, প্রজেক্ট স্ট্রাকচার সম্পর্কে ভালভাবে আলোচনা করেছিলাম। এই ক্লাসে আমরা একটা প্রজেক্ট করবো। সেটা হচ্ছে র‍্যাফেল ড্র প্রজেক্ট। আমরা শুরু থেকে ধরে ধরে সব অ্যানালাইসিস করে এই প্রজেক্টের ব্যাকএন্ড তৈরি করবো। + +এখন যদি আমাদের র‍্যাফেল ড্রয়ের জন্য অ্যাপ্লিকেশন ডিজাইন করতে হয় এতে কি কি ফিচার্স থাকতে পারে একটু দেখি। + +- sell lottery ticket +- update lottery ticket +- delete lottery ticket +- get all tickets +- get ticket by id +- bulk buy (user can buy multiple tickets at a time) +- raffle draw + +এই অ্যাপ্লিকেশন দিয়ে টিকেট বিক্রয় করার যাবে, টিকেট আপডেট করা যাবে, ডিলিট করা যাবে, সব টিকেট একসাথে পাওয়ার জন্য একটা ফিচার, আইডি অনুসারে টিকেট বের করা, একজন ইউজার একসাথে অনেক টিকেট ক্রয় করতে পারে এমন ফিচার, সবশেষে র‍্যাফেল ড্র। + +যেকোনো অ্যাপ্লিকেশন তৈরির পূর্বে আমাদের প্রথম কাজ হলো রিকোয়ারমেন্ট অ্যানালাইসিস। এরপর রিকোয়ারমেন্টসকে এক্সটেন্ড করা। কেন এক্সটেন্ড করবো? কারণ ক্লায়েন্টের বলা সাধারণ ভাষা দিয়ে আমরা অ্যাপ্লিকেশন ডিজাইন করতে পারবো না। আমাদের সেই সাধারণ ভাষাকে টেকনিক্যাল ভাষায় রূপান্তর করতে হবে। এরপর আমাদের কি কি ডাটাবেজ মডেল থাকতে পারে সেগুলো খুঁজে বের করতে হবে। এরপর সেই মডেলগুলোর মধ্যে সম্পর্ক স্থাপন করতে হবে। এখন আমরা অতো জটিলে যাবো না। আমরা প্রথমে আমাদের রিকোয়ারমেন্টগুলো বের করি। + +এখানে যদি আমরা লটারী টিকেট বিক্রি করতে চাই তাহলে আমাদের আগে টিকেট স্টরেজ থাকতে হবে একটা। কারণ ড্র করতে হলে আমার টিকেটের ইনফরমেশন লাগবে, সেগুলো যদি আমরা স্টোরই না করি তাহলে কিভাবে ড্র করবো, আর কিভাবেই বা ইউজারের টিকেট ভেরিফাই করবো। সেজন্য আমরা একটা স্টোরেজ সিস্টেম রাখবো। যদি টিকেট স্টোর করা থাকে তাহলে উপরের সব কাজই আমরা করতে পারবো। তার মানে আমাদের অ্যাপ্লিকেশনে একটাই রিকোয়ারমেন্ট, সেটা হলো `Ticket`। এখন এই টিকেটের শেইপ কেমন হতে পারে? শেইপ বলতে টিকেটের মডেলের মধ্যে কি কি থাকতে পারে সেগুলো একটু দেখি। + +- number (unique) +- username +- price +- timestamp + +প্রথম আমাদের একটা ইউনিক নাম্বার লাগবে টিকেটের। এরপর আমাদের লাগবে যে টিকেট কিনেছে তার নাম। এরপর লাগবে টিকেটের প্রাইস। তারপর টিকেট কখন কিনেছে তার একটা রেকর্ড লাগবে। + +এখন এই মডেলকে আমরা কোডের রূপান্তর করতে চাই। তার জন্য আমরা গত ক্লাসের স্ট্রাকচার এবং ফাইল যেভাবে লিখেছিলাম সেগুলো থাকবে। সাথে এখন আমরা models ফোল্ডারের মধ্যে আমরা Ticket.js নামে একটা ফাইল নিয়ে সেখানে একটা ক্লাস তৈরি করবো। + +```js +// /models/Ticket.js + +const shortid = require('shortid'); + +class Ticket { + /** + * constructor function + * @param {string} username + * @param {number} price + */ + constructor(username, price) { + this.id = shortid.generate(); + this.username = username; + this.price = price; + this.createdAt = new Date(); + this.updatedAt = new Date(); + } +} + +module.exports = Ticket; +``` + +এই ক্লাসের মধ্যে আমরা কন্সট্রাকটর নিবো। সেখানে প্যারামিটার হিসেবে username, price দিবো। আইডি জেনারেট করার জন্য আমরা shortid নামে একটা প্যাকেজ ইনস্টল করে নিবো। বাকি কোড আপনারা বুঝতে পারছেন। কেন username এবং price আমরা প্যারামিটার হিসেবে নিয়েছি। কারণ এই দুইটা ভ্যারিয়েবলই চেইঞ্জ হবে। ইউজার বিভিন্ন হবে আর প্রাইসও বিভিন্নরকম হতে পারে। + +এখন আমরা আমাদের রিকোয়ারমেন্টগুলোর দিকে তাকালে দেখবো আমাদের ডিলিট করার অপশন দরকার। এখন এই ফাইল হচ্ছে সিঙ্গেল টিকেটের। এখান থেকে ডিলিট করতে পারবো না আমরা। আমরা ডিলিট করতে পারবো ডাটাবেজ থেকে যেখানে অনেক টিকেট থাকবে। এই ফাইলে আমরা সর্বোচ্চ আপডেট করতে পারবো। + +এবার আমরা ডাটাবেজের কাছে চলে যায়। এখন তো জাস্ট মডেল তৈরি হলো। এই মডেল তো আর একটা থাকবে না অনেকগুলো থাকবে। সেটা আপাতত আমরা ডাটাবেজের মধ্যে ক্রিয়েট করি। আমরা db ফোল্ডারের মধ্যে db.js নামে একটা ফাইল ক্রিয়েট করবো। + +```js +// /db/db.js + +const Ticket = require('../models/Ticket'); + +class MyDB {} + +const myDB = new MyDB(); +module.exports = myDB; +``` + +আমরা প্রথমে আমাদের মডেলকে ইমপোর্ট করে নিবো। কারণ এটা আমাদের দরকার আছে। এরপর আমরা একটা ক্লাস নিলা। এই ক্লাস আমি এক্সপোর্টও করবো না, মাল্টিপল জায়গায় ব্যবহারও করবো না। করলে কি হবে? আমাদের ইনফরমেশনগুলো ছড়িয়ে ছিটিয়ে যাবে। ইউজার মাল্টিপল ডাটাবেজ ক্রিয়েট করতে পারবে। কিন্তু আমরা চাই আমাদের সমস্ত ইনফরমেশন এক ডাটাবেজে থাকুক। তাই আমরা আমাদের ক্লাসকে একটা অবজেক্টের মধ্যে রেখে সেই অবজেক্টকে এক্সপোর্ট করে দিলাম। জাভাস্ক্রিপ্টের যে মডিউল সিস্টেম তা সিঙ্গেলটোন প্যাটার্ন ফলো করে। এটা আবার কি? সিঙ্গেলটোন প্যাটার্ন মানে হলো কোনো একটা নির্দিষ্ট অবজেক্টকে যেখান থেকেই আপডেট করেন না কেন তা দশ জায়গায় আপডেট হবে না, হবে একটা জায়গাতেই। একেই বলে সিঙ্গেলটোন প্যাটার্ন। এবার আমরা আমাদের ক্লাসের মধ্যে একটা কন্সট্রাকটর নিবো। এখানে আমরা শুধু একটা টিকেটের অ্যারে নিবো। প্রাথমিক অবস্থায় এটা একটা ফাঁকা অ্যারে যার মধ্যে পরবর্তীতে টিকেট রাখবো। + +```js +// /db/db.js + +const Ticket = require('../models/Ticket'); + +class MyDB { + constructor() { + this.tickets = []; + } +} + +const myDB = new MyDB(); +module.exports = myDB; +``` + +এবার প্রথমে আমরা টিকেট ক্রিয়েট করার জন্য একটা মেথড লিখবো। + +```js +// /db/db.js + +const Ticket = require('../models/Ticket'); + +class MyDB { + constructor() { + this.tickets = []; + } + + /** + * Create and save a new ticket + * @param {string} username + * @param {number} price + * @returns {Ticket} return a ticket object + */ + create(username, price) { + const ticket = new Ticket(username, price); + this.tickets.push(ticket); + return ticket; + } +} + +const myDB = new MyDB(); +module.exports = myDB; +``` + +এখানে আমরা যেভাবে কমেন্ট করেছি সেটা হচ্ছে jsDocs. এটা একটা পাওয়ারফুল টুল। আপনি এটার মাধ্যমে আপনার প্যারামিটারগুলোর টাইপ বলে দিতে পারবেন, যেহেতু জাভাস্ক্রিপ্টে টাইপ বলে দেয়ার কোনো সিস্টেম নাই। এখন create ফাংশনের মধ্যে username, price এই দুইটা প্যারামিটার নিলাম। এরপর একটা টিকেট অবজেক্ট তৈরি করলাম টিকেট মডেল থেকে। সেটা টিকেট অ্যারেতে পুশ করে রিটার্ন করে দিলাম। সিম্পল অনেক। + +আমরা এবার একজন ইউজার যেন অনেক টিকেট কিনতে পারে অর্থাৎ বাল্ক টিকেট কেনার একটা মেথড তৈরি করবো। এখানে সব আগের মতো থাকবে শুধু টিকেটের সংখ্যা আসবে। আর যেহেতু একাধিক টিকেট কিনলে টিকেটের আইডি চেইঞ্জ হয় তাই মাল্টিপল টিকেট ক্রিয়েট করতে হবে। + +```js +// /db/db.js + +const Ticket = require('../models/Ticket'); + +class MyDB { + constructor() { + this.tickets = []; + } + + /** + * Create and save a new ticket + * @param {string} username + * @param {number} price + * @returns {Ticket} return a ticket object + */ + create(username, price) { + const ticket = new Ticket(username, price); + this.tickets.push(ticket); + return ticket; + } + + /** + * Create multiple tickets for a single user + * @param {string} username + * @param {number} price + * @param {number} quantity + * @returns {Array} + */ + bulkCreate(username, price, quantity) { + const result = []; + for (let i = 0; i < quantity; i++) { + const ticket = this.create(username, price); + result.push(ticket); + } + return result; + } +} + +const myDB = new MyDB(); +module.exports = myDB; +``` + +এখানে খুব জটিল কোনো কাজ করা হয়নি। কোয়ানটিটি অনুযায়ী একটা লুপ চালিয়ে টিকেট তৈরি করা হয়েছে এবং সব টিকেট একটা অ্যারে আকারে রিটার্ন করা হয়েছে। + +এবার আমরা টিকেট find করার জন্য একটা মেথড বানাবো। + +```js +// /db/db.js + +const Ticket = require('../models/Ticket'); + +class MyDB { + constructor() { + this.tickets = []; + } + + /** + * Create and save a new ticket + * @param {string} username + * @param {number} price + * @returns {Ticket} return a ticket object + */ + create(username, price) { + const ticket = new Ticket(username, price); + this.tickets.push(ticket); + return ticket; + } + + /** + * Create multiple tickets for a single user + * @param {string} username + * @param {number} price + * @param {number} quantity + * @returns {Array} + */ + bulkCreate(username, price, quantity) { + const result = []; + for (let i = 0; i < quantity; i++) { + const ticket = this.create(username, price); + result.push(ticket); + } + return result; + } + + /** + * returns all available tickets + */ + find() { + return this.tickets; + } +} + +const myDB = new MyDB(); +module.exports = myDB; +``` + +আসলে এখানে কোনো কাজই নেই। find মেথড কল হলে আমরা জাস্ট টিকেটের অ্যারেটা রিটার্ন করে দিবো। + +এবার একটা নির্দিষ্ট টিকেটকে কিভাবে আইডি দিয়ে খুঁজে বের করতে পারবো তার একটা মেথড বানাবো। + +```js +// /db/db.js + +const Ticket = require('../models/Ticket'); + +class MyDB { + constructor() { + this.tickets = []; + } + + /** + * Create and save a new ticket + * @param {string} username + * @param {number} price + * @returns {Ticket} return a ticket object + */ + create(username, price) { + const ticket = new Ticket(username, price); + this.tickets.push(ticket); + return ticket; + } + + /** + * Create multiple tickets for a single user + * @param {string} username + * @param {number} price + * @param {number} quantity + * @returns {Array} + */ + bulkCreate(username, price, quantity) { + const result = []; + for (let i = 0; i < quantity; i++) { + const ticket = this.create(username, price); + result.push(ticket); + } + return result; + } + + /** + * returns all available tickets + */ + find() { + return this.tickets; + } +} + +/** + * find ticket by ticket id + * @param {string} ticketId + * @returns {Ticket} + */ + findById(ticketId) { + const ticket = this.tickets.find( + /** + * @param {Ticket} ticket + */ + (ticket) => ticket.id === ticketId + ); + + return ticket; + } + +const myDB = new MyDB(); +module.exports = myDB; +``` + +এখানে আমরা টিকেট অ্যারে থেকে অ্যারের find মেথড ব্যবহার করে নির্দিষ্ট আইডির টিকেট রিটার্ন করবো। + +এবার ইউজারনেইম দিয়ে কিভাবে টিকেট বের করতে পারি সেটার একটা মেথড লিখবো। + +```js +// /db/db.js + +const Ticket = require('../models/Ticket'); + +class MyDB { + constructor() { + this.tickets = []; + } + + /** + * Create and save a new ticket + * @param {string} username + * @param {number} price + * @returns {Ticket} return a ticket object + */ + create(username, price) { + const ticket = new Ticket(username, price); + this.tickets.push(ticket); + return ticket; + } + + /** + * Create multiple tickets for a single user + * @param {string} username + * @param {number} price + * @param {number} quantity + * @returns {Array} + */ + bulkCreate(username, price, quantity) { + const result = []; + for (let i = 0; i < quantity; i++) { + const ticket = this.create(username, price); + result.push(ticket); + } + return result; + } + + /** + * returns all available tickets + */ + find() { + return this.tickets; + } + + /** + * find ticket by ticket id + * @param {string} ticketId + * @returns {Ticket} + */ + findById(ticketId) { + const ticket = this.tickets.find( + /** + * @param {Ticket} ticket + */ + (ticket) => ticket.id === ticketId + ); + + return ticket; + } + + /** + * find all tickets for a given user + * @param {string} username + * @returns {Array} + */ + findByUser(username) { + const tickets = this.tickets.filter( + /** + * @param {Ticket} ticket + */ + (ticket) => ticket.username === username + ); + return tickets; + } +} + +const myDB = new MyDB(); +module.exports = myDB; +``` + +এখানে ফিল্টার মেথড ইউজ করে একজন ইউজারের সমস্ত টিকেট রিটার্ন করার হয়েছে। + +এবার আমরা লিখবো টিকেট আপডেট করার মেথড। + +```js +// /db/db.js + +const Ticket = require('../models/Ticket'); + +class MyDB { + constructor() { + this.tickets = []; + } + + /** + * Create and save a new ticket + * @param {string} username + * @param {number} price + * @returns {Ticket} return a ticket object + */ + create(username, price) { + const ticket = new Ticket(username, price); + this.tickets.push(ticket); + return ticket; + } + + /** + * Create multiple tickets for a single user + * @param {string} username + * @param {number} price + * @param {number} quantity + * @returns {Array} + */ + bulkCreate(username, price, quantity) { + const result = []; + for (let i = 0; i < quantity; i++) { + const ticket = this.create(username, price); + result.push(ticket); + } + return result; + } + + /** + * returns all available tickets + */ + find() { + return this.tickets; + } + + /** + * find ticket by ticket id + * @param {string} ticketId + * @returns {Ticket} + */ + findById(ticketId) { + const ticket = this.tickets.find( + /** + * @param {Ticket} ticket + */ + (ticket) => ticket.id === ticketId + ); + + return ticket; + } + + /** + * find all tickets for a given user + * @param {string} username + * @returns {Array} + */ + findByUser(username) { + const tickets = this.tickets.filter( + /** + * @param {Ticket} ticket + */ + (ticket) => ticket.username === username + ); + return tickets; + } + + /** + * update ticket by id + * @param {string} ticketId + * @param {{username: string, price: number}} ticketBody + * @returns {Ticket} + */ + updateById(ticketId, ticketBody) { + const ticket = this.findById(ticketId); + ticket.username = ticketBody.username ?? ticket.username; + ticket.price = ticketBody.price ?? ticket.price; + ticket.updatedAt = new Date(); + return ticket; + } +} + +const myDB = new MyDB(); +module.exports = myDB; +``` + +এখানে প্রথমে আমরা টিকেটের আইডি দিয়ে টিকেটটা বের করে আনলাম। এরপর আমরা ডাটা আপডেট করার কিছু কোড লিখে দিলাম। এখানে ?? কে বলে Nullish Coalescing Operator. এটা মানে বুঝায় যদি ?? এর বাম পাশের ডাটার ভ্যালু null বা undefined হয় তবে তা ?? এর রাইট সাইডের ডাটা ভ্যালু হবে। আর যদি না হয় তাহলে ওটাতে যে ভ্যালু আছে তাই হবে। এখানেও আমরা যদি টিকেট বডিতে কোনো নাম পাই সেটা আপডেট করবো, নাহয় টিকেটের ইউজারনেইম ইউজ করবো। + +এবার টিকিট ডিলিট করার মেথড লেখার পালা। + +```js +// /db/db.js + +const Ticket = require('../models/Ticket'); + +class MyDB { + constructor() { + this.tickets = []; + } + + /** + * Create and save a new ticket + * @param {string} username + * @param {number} price + * @returns {Ticket} return a ticket object + */ + create(username, price) { + const ticket = new Ticket(username, price); + this.tickets.push(ticket); + return ticket; + } + + /** + * Create multiple tickets for a single user + * @param {string} username + * @param {number} price + * @param {number} quantity + * @returns {Array} + */ + bulkCreate(username, price, quantity) { + const result = []; + for (let i = 0; i < quantity; i++) { + const ticket = this.create(username, price); + result.push(ticket); + } + return result; + } + + /** + * returns all available tickets + */ + find() { + return this.tickets; + } + + /** + * find ticket by ticket id + * @param {string} ticketId + * @returns {Ticket} + */ + findById(ticketId) { + const ticket = this.tickets.find( + /** + * @param {Ticket} ticket + */ + (ticket) => ticket.id === ticketId + ); + + return ticket; + } + + /** + * find all tickets for a given user + * @param {string} username + * @returns {Array} + */ + findByUser(username) { + const tickets = this.tickets.filter( + /** + * @param {Ticket} ticket + */ + (ticket) => ticket.username === username + ); + return tickets; + } + + /** + * update ticket by id + * @param {string} ticketId + * @param {{username: string, price: number}} ticketBody + * @returns {Ticket} + */ + updateById(ticketId, ticketBody) { + const ticket = this.findById(ticketId); + ticket.username = ticketBody.username ?? ticket.username; + ticket.price = ticketBody.price ?? ticket.price; + ticket.updatedAt = new Date(); + return ticket; + } + + /** + * delete ticket from db + * @param {string} ticketId + */ + deleteById(ticketId) { + const index = this.tickets.findIndex( + /** + * @param {Ticket} ticket + */ + (ticket) => ticket.id === ticketId + ); + + if (index !== -1) { + this.tickets.splice(index, 1); + return true; + } else { + return false; + } + } +} + +const myDB = new MyDB(); +module.exports = myDB; +``` + +প্রথমে আমরা চেক করবো যে আইডিটা দেয়া হলো সেটা ইনডেক্স কতো। যদি ইনডেক্স -1 না হয় অর্থাৎ আইডি পাওয়া যায় তবে splice মেথড ব্যবহার করে তা ডিলিট করে দিবো। + +এবার সবশেষ ড্র এর জন্য মেথড বানানো বাকি। + +```js +// /db/db.js + +const Ticket = require('../models/Ticket'); + +class MyDB { + constructor() { + this.tickets = []; + } + + /** + * Create and save a new ticket + * @param {string} username + * @param {number} price + * @returns {Ticket} return a ticket object + */ + create(username, price) { + const ticket = new Ticket(username, price); + this.tickets.push(ticket); + return ticket; + } + + /** + * Create multiple tickets for a single user + * @param {string} username + * @param {number} price + * @param {number} quantity + * @returns {Array} + */ + bulkCreate(username, price, quantity) { + const result = []; + for (let i = 0; i < quantity; i++) { + const ticket = this.create(username, price); + result.push(ticket); + } + return result; + } + + /** + * returns all available tickets + */ + find() { + return this.tickets; + } + + /** + * find ticket by ticket id + * @param {string} ticketId + * @returns {Ticket} + */ + findById(ticketId) { + const ticket = this.tickets.find( + /** + * @param {Ticket} ticket + */ + (ticket) => ticket.id === ticketId + ); + + return ticket; + } + + /** + * find all tickets for a given user + * @param {string} username + * @returns {Array} + */ + findByUser(username) { + const tickets = this.tickets.filter( + /** + * @param {Ticket} ticket + */ + (ticket) => ticket.username === username + ); + return tickets; + } + + /** + * update ticket by id + * @param {string} ticketId + * @param {{username: string, price: number}} ticketBody + * @returns {Ticket} + */ + updateById(ticketId, ticketBody) { + const ticket = this.findById(ticketId); + ticket.username = ticketBody.username ?? ticket.username; + ticket.price = ticketBody.price ?? ticket.price; + ticket.updatedAt = new Date(); + return ticket; + } + + /** + * delete ticket from db + * @param {string} ticketId + */ + deleteById(ticketId) { + const index = this.tickets.findIndex( + /** + * @param {Ticket} ticket + */ + (ticket) => ticket.id === ticketId + ); + + if (index !== -1) { + this.tickets.splice(index, 1); + return true; + } else { + return false; + } + } + + /** + * find winners + * @param {number} winnerCount + * @returns {Array} + */ + draw(winnerCount) { + const winnerIndices = new Array(winnerCount); + let index = 0; + while (index < winnerCount) { + let winnerIndex = Math.floor(Math.random() * this.tickets.length); + if (!winnerIndices.includes(winnerIndex)) { + winnerIndices[index++] = winnerIndex; + continue; + } + } + + const winners = winnerIndices.map((index) => this.tickets[index]); + return winners; + } +} + +const myDB = new MyDB(); +module.exports = myDB; +``` + +আমরা প্রথমে আর্গুমেন্ট আকার কতজন উইনার চাইছি তত নাম্বার দিবো। সেই লেংথের একটা অ্যারে বানিয়ে নিলাম। ইনিশিয়ালি ইনডেক্স ধরে নিলাম ০। এবার একটা হোয়াইল লুপ চালাবো যতক্ষণ পর্যন্ত ইনডেক্স winnerCount এর চেয়ে ছোট হবে। সেই লুপে কি থাকবে। প্রথমে টিকেট লেংথের উপর ভিত্তি করে আমরা একটা র‍্যান্ডম ইনডেক্স জেনারেট করবো। এরপর যদি winnerIndices এ এই ইনডেক্স না থাকে তাহলে সেখানে তা দিয়ে দিবো। শেষে আমরা winnerIndices কে ম্যাপ করে সেই ইনডেক্স নাম্বার অনুযায়ী টিকেট ইনফরমেশন বিজয়ী হিসেবে রিটার্ন করবো। + +এবার আমরা আমাদের এই ফাংশনগুলো ঠিকঠাক কাজ করছে কিনা সেটা চেক করার জন্য test ফোল্ডারের মধ্যে test.js নামে একটা ফাইল নিয়ে কিছু ডামী ডাটা দিয়ে টেস্ট করে দেখতে পারি। + +```js +// /test/test.js + +const myDB = require('../db/db'); +myDB.create('user 1', 10); +myDB.create('user 2', 10); +myDB.create('user 3', 10); +myDB.create('user 4', 10); +myDB.create('user 5', 10); +const bulk = myDB.bulkCreate('test', 10, 5); +console.log('Bulk', bulk); +const tickets = myDB.find(); +console.log('All Tickets', tickets); +const winners = myDB.draw(3); +console.log('Winners', winners); +``` + +এই ফাইলটা রান করালে আমরা বুঝতে পারবো আমাদের ফাংশন ঠিক আছে কিনা। দেখলাম ঠিক আছে। + +এবার আমরা আমাদের অ্যাপ্লিকেশনের রাউটস তৈরি করতে পারি। একটা অ্যাপ্লিকেশনের জন্য রাউটস খুবই গুরুত্বপূর্ণ। কারণ রাউটসের উপর ভিত্তি করে আমাদের অনেক কাজ করতে হয়। তাই আমাদের অ্যাপ্লিকেশনে কয়টা রাউটস থাকতে পারে সেটা আগে বের করে আনতে হবে। আমরা আমাদের ইউজারকে যতগুলো রিসোর্সের এক্সেস দিবো ততটা রাউট তৈরি করতে হবে। ধরেন আমার অ্যাপ্লিকেশনে ৪০টা মডেল আছে। এখন প্রতিটা মডেলের জন্য ৫০০টা করে রাউটস তৈরি করতে হবে। আমাদের সেই ৫০০ \* ৪০ = ২০০০০ টা রাউটস একইভাবে বসে বসে লিখতে হবে। এটাই ব্যাকএন্ডের সমস্যা। কিন্তু কিছু করার নেই। আমাদের এটা করতেই হবে। + +এবার আমরা আমাদের রাউটগুলো লিখে ফেলি। + +- /tickets/t/:ticketId GET - find single ticket +- /tickets/t/:ticketId PATCH - update ticket by id +- /tickets/t/:ticketId DELETE - delete ticket by id +- /tickets/u/:username GET - find tickets for a given user +- /tickets/sell - create tickets +- /tickets/bulk - bulk sell ticket +- /tickets/draw - find winners +- /tickets/ - find all tickets + +এখানে একটা জিনিস খেয়াল রাখতে হবে যেটা আমাদের কমন পাথ /tickets এটা আমাদের সবার নিচে লিখতে হবে। যদি উপরে লিখি তাহলে অনেকসময় অ্যাপ্লিকেশন এরপর কি আছে তা খেয়াল না করে /tickets এর রেজাল্ট রিটার্ন করে দিবে। + +এবার আমরা routes ফোল্ডারের মধ্যে এই রাউটগুলো লিখবো। + +```js +// /routes/tickets.js + +const router = require('express').Router(); +const db = require('../db/db'); + +module.exports = router; +``` + +এখন `/tickets/t/:ticketId` এর তিনটা মেথড আছে। আমরা চাইলে তিনটা মেথডকে দুইভাবে লিখতে পারি। নিচে দুইটা পদ্ধতিই দেয়া হলো। কিন্তু আমরা প্রথম পদ্ধতি ব্যবহার করবো। + +```js +router.get('/t/:ticketId', (req, res) => {}); +router.patch('/t/:ticketId', (req, res) => {}); +router.delete('/t/:ticketId', (req, res) => {}); +``` + +অথবা + +```js +router + .route('/t/:ticketId') + .get(() => {}) + .patch(() => {}) + .delete(() => {}); +``` + +এই ফাইলটা আমরা app ফোল্ডারের routes.js এ ব্যবহার করবো। + +```js +// /app/routes.js + +const router = require('express').Router(); + +router.use('/api/v1/tickets', require('../routes/ticket')); + +router.get('/health', (_req, res) => { + res.status(200).json({ message: 'Success' }); +}); + +module.exports = router; +``` + +আপনারা লক্ষ্য করলে দেখবেন tickets.js এ আমরা /tickets না লিখে এর পরবর্তী অংশ থেকে শুরু করেছি। কারণ আমরা আমাদের /app/routes.js এ আর্গুমেন্ট আকারে '/api/v1/tickets' এই পাথটা দিয়ে রেখেছি। তাই আমরা tickets.js এ এর পরবর্তী অংশ থেকে লিখতে পারবো। + +এবার আমাদের রাউটসগুলো একে একে সিরিয়ালি যেভাবে লিখেছিলাম সেভাবে কোড করি। + +```js +// /routes/tickets.js + +const router = require('express').Router(); +const db = require('../db/db'); + +router.get('/t/:ticketId', (req, res) => { + const ticketId = req.params.ticketId; + const ticket = db.findById(ticketId); + res.status(200).json(ticket); +}); + +module.exports = router; +``` + +এখানে আমরা params থেকে টিকেটের আইডি পাবো। এরপর আমাদের ডাটাবেজ থেকে সেই আইডি দিয়ে টিকেটটা বের করে আনবো json আকারে। + +এভাবে বাকিসব লিখে ফেলি। + +```js +// /routes/tickets.js + +const router = require('express').Router(); +const db = require('../db/db'); + +router.get('/t/:ticketId', (req, res) => { + const ticketId = req.params.ticketId; + const ticket = db.findById(ticketId); + res.status(200).json(ticket); +}); + +router.patch('/t/:ticketId', (req, res) => { + const ticketId = req.params.ticketId; + const updatedTicket = db.updateById(ticketId, req.body); + console.log(updatedTicket); + res.status(200).json({ message: 'Updated Successfully', updatedTicket }); +}); + +router.delete('/t/:ticketId', (req, res) => { + const ticketId = req.params.ticketId; + db.deleteById(ticketId); + res.status(203).send(); +}); + +router.get('/u/:username', (req, res) => { + const username = req.params.username; + const tickets = db.findByUser(username); + res.status(200).json(tickets); +}); + +router.post('/sell', (req, res) => { + const { username, price } = req.body; + const ticket = db.create(username, price); + res.status(201).json({ message: 'Ticket created successfully', ticket }); +}); +router.post('/bulk', (req, res) => { + const { username, price, quantity } = req.body; + const tickets = db.bulkCreate(username, price, quantity); + res + .status(201) + .json({ message: 'Bulk ticket created successfully', tickets }); +}); +router.get('/draw', (req, res) => { + const winnerCount = req.query.wc ?? 3; + const winners = db.draw(winnerCount); + res.status(200).json(winners); +}); +router.get('', (req, res) => { + const tickets = db.find(); + res.status(200).json(tickets); +}); + +module.exports = router; +``` + +এবার আমরা আমাদের অ্যাপ্লিকেশনকে পোস্টম্যানে টেস্ট করে দেখবো। পোস্টম্যানের বিকল্প হিসেবে vs code এ এক্সটেনশন হিসেবে thunder client আছে। আমি এখানে thunder client ব্যবহার করছি। + +প্রথমে আমরা আমাদের /health রাউট টেস্ট করবো। + +![health](./images/health.png) + +এবার আমরা /api/v1/tickets এ হিট করলে দেখবো একটা ফাঁকা অ্যারে দেখাচ্ছে। খুবই স্বাভাবিক কারণ আমরা এখনও কিছু ক্রিয়েট করিনি। + +![tickets-init](./images/tickets1.png) + +এবার আমরা টিকেট ক্রিয়েট করবো। /api/v1/tickets/sell এর মাধ্যমে। + +![sell](./images/sell.png) + +টিকেট ক্রিয়েট হয়ে গেছে। এবার আরো কয়েকটা করি। + +এবার যদি আমরা আমাদের /api/v1/tickets এ হিট করি দেখবো আমাদের ফাঁকা অ্যারে ভরে গেছে টিকেটে। + +![tickets-all-1](./images/ticketsAll-1.png) + +এবার bulk টেস্ট করার পালা। + +![bulk](./images/bulk.png) + +আমাদের টিকেটের মধ্যেও এগুলো চলে এসেছে। + +![tickets-all-2](./images/ticketsAll-2.png) + +আমরা বাল্কে আরো একটা ডাটা পুট করলাম ১০টা টিকেটের। + +এবার একটু draw ট্রাই করা যাক। দেখি কে উইনার। আমরা কয়জন উইনার চাই তা কুয়েরি স্ট্রিং আকারে দিয়ে দিবো। আর না দিলে বাই ডিফল্ট তিনজন উইনার রিটার্ন করবে। প্রথমে আমরা কিছু না দিয়ে দেখি। + +![draw-1](./images/draw-1.png) + +এবার আমরা চাই একজন উইনার। + +![draw-2](./images/draw-2.png) + +এটাও কাজ করছে। + +এবার আমরা আইডি দিয়ে টিকেট খুঁজে বের করি। + +![findbyid](./images/findbyid.png) + +এবার এটাকে আপডেট করবো। + +![update](./images/update.png) + +টিকেট অ্যারেতে গেলে দেখা যাবে সেখানেও আপডেট হয়ে গেছে। + +![tickets](./images/ticketsAll-3.png) + +আমরা এবার ডিলিট অপারেশন চালাবো। আমরা Hridoy নামের ইউজারকে ডিলিট করবো। প্রথমে তার আইডি নিতে হবে। + +![delete](./images/delete.png) +![tickets](./images/ticketsAll-4.png) + +দেখা যাচ্ছে সেই আইডিটা আর নেই অ্যারেতে। + +এবার সর্বশেষ আমরা দেখবো ইউজার নেইম দিয়ে কিভাবে টিকেট পাবো। + +![findbyuser](./images/username.png) + +এটাও সাক্সেসফুল। তার মানে আমাদের অ্যাপ্লিকেশন কমপ্লিট। + +এরপরের ক্লাসে ডাটাবেইজ নিয়ে আলোচনা হবে। তার জন্য আপনাদের একটা টাস্ক করে আসতে হবে। আপনারা [mongodb docs](https://www.mongodb.com/docs/manual/crud/) এ গিয়ে Insert documents, Query documents, Update documents and Delete documents এগুলো স্টাডি করবেন হাতে কলমে। + +## Resource for this lecture + +এই লেকচারের সমস্ত রিসোর্স [লেকচার ১৭](../../resources/lecture-17/README.md) এ পাবেন। + +## Source Code + +এই লেকচারের সোর্স কোডসমূহ এই [লিংক](../../src/raffle-draw/) এ পাবেন। + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/full-stack-army/class-overview/Lecture-17/images/bulk.png b/full-stack-army/class-overview/Lecture-17/images/bulk.png new file mode 100644 index 0000000..be55546 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-17/images/bulk.png differ diff --git a/full-stack-army/class-overview/Lecture-17/images/delete.png b/full-stack-army/class-overview/Lecture-17/images/delete.png new file mode 100644 index 0000000..91dbd40 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-17/images/delete.png differ diff --git a/full-stack-army/class-overview/Lecture-17/images/draw-1.png b/full-stack-army/class-overview/Lecture-17/images/draw-1.png new file mode 100644 index 0000000..ff806d1 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-17/images/draw-1.png differ diff --git a/full-stack-army/class-overview/Lecture-17/images/draw-2.png b/full-stack-army/class-overview/Lecture-17/images/draw-2.png new file mode 100644 index 0000000..924d93c Binary files /dev/null and b/full-stack-army/class-overview/Lecture-17/images/draw-2.png differ diff --git a/full-stack-army/class-overview/Lecture-17/images/findbyid.png b/full-stack-army/class-overview/Lecture-17/images/findbyid.png new file mode 100644 index 0000000..2234c1d Binary files /dev/null and b/full-stack-army/class-overview/Lecture-17/images/findbyid.png differ diff --git a/full-stack-army/class-overview/Lecture-17/images/health.png b/full-stack-army/class-overview/Lecture-17/images/health.png new file mode 100644 index 0000000..2666213 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-17/images/health.png differ diff --git a/full-stack-army/class-overview/Lecture-17/images/sell.png b/full-stack-army/class-overview/Lecture-17/images/sell.png new file mode 100644 index 0000000..dd2dc2f Binary files /dev/null and b/full-stack-army/class-overview/Lecture-17/images/sell.png differ diff --git a/full-stack-army/class-overview/Lecture-17/images/sell2.png b/full-stack-army/class-overview/Lecture-17/images/sell2.png new file mode 100644 index 0000000..58402d0 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-17/images/sell2.png differ diff --git a/full-stack-army/class-overview/Lecture-17/images/tickets1.png b/full-stack-army/class-overview/Lecture-17/images/tickets1.png new file mode 100644 index 0000000..de4f25e Binary files /dev/null and b/full-stack-army/class-overview/Lecture-17/images/tickets1.png differ diff --git a/full-stack-army/class-overview/Lecture-17/images/ticketsAll-1.png b/full-stack-army/class-overview/Lecture-17/images/ticketsAll-1.png new file mode 100644 index 0000000..7591e8e Binary files /dev/null and b/full-stack-army/class-overview/Lecture-17/images/ticketsAll-1.png differ diff --git a/full-stack-army/class-overview/Lecture-17/images/ticketsAll-2.png b/full-stack-army/class-overview/Lecture-17/images/ticketsAll-2.png new file mode 100644 index 0000000..54644ef Binary files /dev/null and b/full-stack-army/class-overview/Lecture-17/images/ticketsAll-2.png differ diff --git a/full-stack-army/class-overview/Lecture-17/images/ticketsAll-3.png b/full-stack-army/class-overview/Lecture-17/images/ticketsAll-3.png new file mode 100644 index 0000000..ae7e49d Binary files /dev/null and b/full-stack-army/class-overview/Lecture-17/images/ticketsAll-3.png differ diff --git a/full-stack-army/class-overview/Lecture-17/images/ticketsAll-4.png b/full-stack-army/class-overview/Lecture-17/images/ticketsAll-4.png new file mode 100644 index 0000000..1560887 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-17/images/ticketsAll-4.png differ diff --git a/full-stack-army/class-overview/Lecture-17/images/update.png b/full-stack-army/class-overview/Lecture-17/images/update.png new file mode 100644 index 0000000..908f90a Binary files /dev/null and b/full-stack-army/class-overview/Lecture-17/images/update.png differ diff --git a/full-stack-army/class-overview/Lecture-17/images/username.png b/full-stack-army/class-overview/Lecture-17/images/username.png new file mode 100644 index 0000000..b3a67b2 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-17/images/username.png differ diff --git a/full-stack-army/class-overview/Lecture-18/README.md b/full-stack-army/class-overview/Lecture-18/README.md new file mode 100644 index 0000000..70111a4 --- /dev/null +++ b/full-stack-army/class-overview/Lecture-18/README.md @@ -0,0 +1,92 @@ +# Lecture 18 - Backend 5 | Understand The Concepts of Database + +ব্যাকএন্ড ডেভেলপমেন্ট করতে গেলে আমাদের প্রাথমিক অবস্থায় ডাটাবেজের একদম গভীরে না গেলেও একটা নির্দিষ্ট সীমা পর্যন্ত স্টাডি করতে হয়। এরপর সেই নির্দিষ্ট সীমায় গেলে আরো বেশি স্টাডি করতে হয়। ডাটাবেজ হলো ব্যাকএন্ড ডেভেলপমেন্টের সবচেয়ে ক্রিটিকাল একটা পার্ট। এটা নিয়ে পড়াশোনার শেষ নেই। সারাজীবন করতে চাইলে তাও পারবেন। কিন্তু শেষ হবে না। ডাটাবেজ কেন এত গুরুত্বপূর্ন? কারণ পৃথিবীটা চলছে ডাটার উপর। ডাটা কিভাবে ক্রিয়েট করতে হবে, কিভাবে স্কেল করতে হবে, কিভাবে মাল্টি রিজিওনে আমাদের ডাটা সমানভাবে রীড, রাইট এবং আপডেট করতে পারবে এগুলো নিয়েই মূলত চলছে। এই ডাটার সিকিউরিটিও অনেক বড় একটা বিষয়। আমরা একটু এদিক থেকে ওদিক ভুল করলেই ডাটা হ্যাকররা চুরি করে ফেলবে। যদি ডাটাবেজ ম্যানেজ করার দায়িত্ব ব্যাকএন্ড ডেভেলপারের না। কিন্তু যদি ভাল একটা জ্ঞান না থাকে তাহলে কোথায় কোথায় ডাটা লিক হতে পারে, কোথায় কোথায় হ্যাকাররা অ্যাটাক করতে পারে সেটা আমরা ধরতে পারবো না। যেকারণে সিকিউরড অ্যাপ্লিকেশন তৈরি করাটা অনেক কঠিন হয়ে পড়ে। সুতরাং আমাদের দায়িত্ব না এমন একটা সেক্টরে আমাদের স্টাডি করতে হবে। বিগিনার লেভেলে আমাদের অতো গভীরে যেতে না হলেও যতোই আমরা বিগিনার থেকে ইন্টারমডিয়েট, ইন্টারমিডিয়েট থেকে অ্যাডভান্সড লেভেলে যাবো ততোই আমাদের দরকার পড়বে ডাটাবেজের অনেক গভীরে গিয়ে এর আর্কিটেকচারটা বুঝা, যাতে আমরা বুঝতে পারি কিভাবে কোড করলে আমাদের ডাটা লিক হওয়ার কোনো সম্ভাবনা থাকবে না। + +আমরা ডাটাবেজের নাম কমবেশি সবাই শুনলেও আমরা ডাটাবেজ সম্পর্কে খুব বেশি কেউই জানিনা। আজকের লেকচারে আমরা ডাটাবেজ সম্পর্কে একটা ধারণা নেয়ার চেষ্টা করবো। + +সাধারণত ব্যাকএন্ড ডেভেলপার হিসেবে আমাদের এই সিদ্ধান্ত নিতে হয় না যে আমরা কোনো ডাটাবেইজ ব্যবহার করবো। এই দায়িত্ব সল্যুশন আর্কিটেক্টের। সল্যুশন আর্কিটেক্ট বিজনেস রিকোয়ারমেন্ট অ্যানালাইসিস করে ঐ বিজনেস রিকোয়ারমেন্টের জন্য কোন ধরণের ডাটাবেজ ঠিক হবে সেটা ডিসিশন নিবে। ব্যাকএন্ড ডেভেলপার হিসেবে আমাদের দায়িত্ব হলো ঐ ডাটাবেজকে ইমপ্লিমেন্ট করা। কিন্তু যেহেতু আমরা এই কোর্সে সল্যুশন আর্কিটেক্ট নিয়ে কিছুটা জানবো যতটা না জানলে নয়, তাই ধরে নিচ্ছি আমিই সব ডিসিশন নিবো। আমি একাই ক্লাউড ইঞ্জিনিয়ার, আমি একাই ডেভঅপ্স, আমি একাই সল্যুশন আর্কিটেক্ট, আমি একাই ডেভেলপার। তো সে হিসেবে কি কি ধরণের ডাটাবেজ মার্কেটে আছে, কোন কাজের জন্য কোন ধরণের ডাটাবেজ ব্যবহার হয় তার একটা ছোট জ্ঞান যদি আমাদের থাকে তবে সবচেয়ে সহজ হয় ইন্টারভিউ ক্র্যাক করতে। আমরা সাধারণত ডাটাবেজ বলতে বুঝি SQL এবং NoSQL Database. SQL বলতে আমরা বুঝি MySQL, PostgreSQL আর NoSQL বলতে বুঝি MongoDB। যদি আমাদের অন্যান্য ডাটাবেজ সম্পর্কেও ন্যুনতম ধারণা থাকে তাহলে আমাদের জন্য ইন্টারভিউ ক্র্যাক করতে সুবিধা হবে। প্রোগ্রামিং ল্যাঙ্গুয়েজের যেমন প্যারাডাইম আছে, ডাটাবেজেরও প্যারাডাইম আছে। আমরা যদি ইন্টারনেটে সার্চ করি আমরা সাত ধরণের ডাটাবেজ প্যারাডাইম পাবো। এগুলো হলোঃ + +1. Key Value Database - Key Value ডাটাবেজ একদম জাভাস্ক্রিপ্ট অবজেক্ট বা পাইথনের ডিকশনারির মতো। এটা ইন মেমোরি ডাটাবেজ। তার মানে এটা র‍্যামের মধ্যে ডাটা স্টোর করে। তবে সমস্যা হলো এটা স্টেবল না। পিসি কোনো কারণে রিস্টার্ট বা শাট ডাউন হয়ে গেলে ডাটা হারিয়ে যাবে। এই প্যারাডাইমের মধ্যে [Redis](https://redis.io/), [MEMcached](https://www.memcached.org/), [Amazon DynamoDB](https://aws.amazon.com/dynamodb/) এই তিনটা ডাটাবেজ খুব জনপ্রিয়। বাইরের কোম্পানিতে যদি জব করতে যান তাহলে অবশ্যই ডায়নামোডিবি সম্পর্কে জানতে হবে। এটা আসলে কোন ক্যাটাগরিতে ফেলা যায় সেটা বুঝার কোনো জায়গা নাই। এর অফিসিয়াল সাইটে একে NoSQL বলা হলেও এর বিহেভিয়ার অনুযায়ী একে key value databse ও বলা যায় আবার wide-column database ও বলা যায়। যদি কোনো জায়গায় কন্টিনিউ ডাটা হিট হচ্ছে, তাহলে দ্রুত ডাটা রীড করার জন্য এটা ব্যবহার করা হয়। এটা অনেক ফাস্ট। এটা শেখাটা খুব সহজ। কিন্তু আমার বিজনেস রিকোয়ারমেন্ট অনুসারে এই ডাটাবেজে ফিট করাটা হচ্ছে খুব কঠিন। এতে রয়েছে পার্টিশন কী, হ্যাশ কী এবং অ্যাট্রিবিউটস। + ![key value database](./images/key_value.png) + +2. Wide-Column Database - এটা অনেকটা কী ভ্যালু ডাটাবেজের মতোই শুধু এর সাথে একটা সেকেন্ড ডাইমেনশন যোগ করে দিবেন। আমরা ডায়নামোডিবির বেলায় যেটা বললাম এখানে পার্টিশন কী, হ্যাশ কী এবং অ্যাট্রিবিউটস রয়েছে, সেরকম এই ডাটাবেজের ক্ষেত্রে আপনি একটা কী এর বিপরীতে মাল্টিপল অ্যাট্রিবিউটস ইউজ করতে পারবেন। ডায়নামোডিবিতেও পারবেন। তাই উপরের প্যারায় বলেছিলাম এটাকে wide-column database ও বলে। এই প্যারাডাইমের মধ্যে সবচেয়ে জনপ্রিয় হচ্ছে [Cassandra](https://cassandra.apache.org/_/index.html)। NoSQL ডাটাবেজের মধ্যে MongoDB এর পর সবচেয়ে জনপ্রিয় ডাটাবেজ হলো Cassandra। MongoDB তে আপনি যদি Atlas cloud service ব্যবহার করেন তাহলে ঠিক আছে। কিন্তু যদি কমিউনিটি এডিশনে আপনি অ্যাপ্লিকেশনকে স্কেল করতে চান বা ৯৯.৯৯% আপটাইম প্রোভাইড করতে চান তবে আপনাকে অনেক ঝক্কি পোহাতে হবে। এসব কাজের জন্য Cassandra বেস্ট। এছাড়াও রয়েছে [Apache Hbase](https://hbase.apache.org/)। যদিও Cassandra এবং MongoDB দুইটাই NoSQL তবে দুইটার কনসেপ্ট টোটালি ভিন্ন। + ![wide-column](./images/wide_column.png) + +3. Document Oriented Database - Document Oriented Database বলতে আমরা বুঝি [MongoDB](https://www.mongodb.com/)। যদিও আমরা MongoDB বলতে বুঝি NoSQL। কিন্তু এটা ডাটাবেজের কোনো টাইপের মধ্যেই পড়ে না। এটার মানে হচ্ছে যেটা সিক্যুয়্যাল না। এরকম অনেক NoSQL ডাটাবেজ আছে, যেমনঃ Cassandra, DynamoDB, IndexedDB, Apache Hbase, Redis, MemCached। কিন্তু যদি আমরা MongoDB কে একটা টাইপ দেয়ার চেষ্টা করি সেটা হলো Document Oriented Database। এখন Document Oriented Database বলতে কি বুঝায় সেটাই আমাদের আজকের মূল আলোচ্য বিষয়। সেটা নিয়ে আমরা বিস্তারিত আলোচনা করবো। তাই এখন এটা এড়িয়ে যাচ্ছি। এছাড়াও এই টাইপের ডাটাবেজের মধ্যে [Firebase](https://firebase.google.com/docs/firestore/) কেও রাখা যায়। +4. The Relational Database - আমরা ডাটাবেজ বলতেই বুঝতাম রিলেশনাল ডাটাবেজ। কি কি আছে এতে? এতে আছে [MySQL](https://www.mysql.com/), [PostgreSQL](https://www.postgresql.org/), [Microsoft SQL](https://docs.microsoft.com/en-us/sql/) ইত্যাদি। যেখানে টেবিল রয়েছে, রো রয়েছে, কলাম রয়েছে সেগুলোই রিলেশনাল ডাটাবেজ। আর এখানে একটা কমন কুয়েরি ল্যাঙ্গুয়েজ রয়েছে, যেটা দিয়ে ডাটা কুয়েরি করে আনা হয়। +5. Graph Database - এটা নিয়ে [লেকচার ১৫](../Lecture-15/README.md) তে আলোচনা করা হয়েছে। আপনারা আশা করি সেটা সম্পর্কে অলরেডি ভালভাবেই বুঝেছেন। না বুঝে থাকলে একটু লেকচার ১৫ তে গিয়ে পড়ে নিবেন। +6. A Full Text Search Engine - একে আবার ইনডেক্স ডাটাবেজও বলা হয়। ইনডেক্সিং করা এর কাজ। ইনডেক্স করা হয় মূলত কোনো একটা ডাটাকে সার্চ করে আনার জন্য। হ্যাশিং অ্যালগরিদম এখানে কাজ করে, বা হ্যাশ টেবিলের মতো একটা কনসেপ্ট এখানে কাজ করে। সেটা বিস্তারিত আমরা জানবো যখন ইনডেক্স নিয়ে জানবো। এখন আমাদের এত গভীরে যাওয়ার প্রয়োজন নেই। ধরেন আমাদের কোনো প্রোডাক্টের ৫০টা প্রোপার্টিজ রয়েছে। ঐ ৫০টা প্রোপার্টিজ আমাদের ডাটাবেজে স্টোর করে রাখতে হবে। কিন্তু যখন আমরা সার্চ করছি তখন আমরা হয় টাইটেল দিয়ে সার্চ করছি, নাহয় ট্যাগ দিয়ে সার্চ করছি, অথবা ক্যাটাগরি দিয়ে সার্চ করছি, অথবা প্রাইস দিয়ে সার্চ করছি। এখানে আরো অনেক অ্যাট্রিবিউটস থাকতে পারে। কিন্তু আমরা সার্চ করার জন্য নির্দিষ্ট কিছু প্রোপার্টিজ ব্যবহার করি। এখন যেগুলো দিয়ে আমরা সার্চ করি সেই সার্চিং কনসেপ্টগুলোকে আমরা ইনডেক্স করে রাখতে পারি সহজে এক্সেস করার জন্য। যখন আমরা ইনডেক্স করি তখন এগুলো একটা হ্যাশ টেবিলের মধ্যে চলে যায় যেখানে আমাদের রীড অপারেশন কমপ্লিট হতে O(1) পরিমাণ সময় লাগে। তার মানে এটা খুব দ্রুত কাজ করে ইনডেক্স করার মাধ্যমে। আমরা যেকোনো ডাটাবেজে ইনডেক্স করতে পারি, কিন্তু A Full Text Search Engine ডাটাবেজগুলো ইনডেক্স করার কাজেই ব্যবহৃত হয়। এরা অরিজিনাল ডাটা স্টোর করে না, তাই এরা খুবই ফাস্ট হয়। এখানে শুধুমাত্র সার্চ করার অপারেশনগুলোই পাওয়া যাবে। আপনি চাইলে এই ডাটাবেজগুলোকে নরমাল ডাটাবেজগুলোর মতো ব্যবহার করতে পারেন, কিন্তু এগুলোর খরচ অনেক বেশি। যে কারণে শুধু ইনস্ট্যান্ট সার্চ রেজাল্ট দেয়ার জন্য যা যা দরকার শুধুমাত্র সেগুলোই এখানে ব্যবহার করা হয়। এই ধরণের ডাটাবেজের মধ্যে রয়েছে [Algolia](https://www.algolia.com/) যেটা নতুন এসেছে। এটা Open AI3 ব্যবহার করে। যার কারণে আপনি যেটা সার্চ করেছেন সেটা পুরোপুরি ম্যাচ না করলেও যদি তার ভাবার্থ অনুযায়ী কিছু থাকে ডাটাবেজে সেটা খুঁজে বের করে আনার পাওয়ার রাখে Algolia। এটা নেক্সট জেনারেশন সার্চ ইঞ্জিন। আর বহু বছর ধরে মার্কেটে রয়েছে [Elastic Search](https://www.elastic.co/elasticsearch/)। এটা Old জেনারেশন। এখানে অবশ্যই টেক্সট ম্যাচ হতে হবে। ম্যাচ না হলে ডাটা বের করতে পারবে না। তবে এটা ইন্ডাস্ট্রিয়ালি প্রমাণিত। প্রচুর ব্যবহার হয় এটা। +7. Multi Model Database - এর মধ্যে আছে [Fauna DB](https://fauna.com/)। মেইনলি ডাটাবেজ থেকে GraphQL এ রূপান্তরিত করার কাজ করে থাকে। এটা অনেকটা প্রোগ্রামিং ল্যাঙ্গুয়েজের খুব কাছাকাছি। এখানে যেকোনো কিছু হতে পারে। কিন্তু এখনও পর্যন্ত এটা সেরকম পপুলার না। + +প্রাথমিক অবস্থায় আমাদের এত ডাটাবেজ সম্পর্কে অতো বেশি জানতে হবে না। আমাদের এটুকু জানলেই হবে যে এরকম একটা ডাটাবেজ রয়েছে, আর এই টাইপের কাজ করার জন্য এই ডাটাবেজ ইউজ করা হয়। আমরা প্রথমে শিখবো MongoDB। পরবর্তীতে আমরা যখন ভাল মাপের ডেভেলপার হয়ে যাবো, যখন আমাদের মধ্যে বিশ্বাস আসবে MongoDB ব্যবহার করে Data Model, Aggregation, Transaction সহ যা যা করা দরকার সবই করতে পারি, আমার কোনো সমস্যা হয় না, তখন আমরা অন্য নতুন একটা ডাটাবেজ এক্সপ্লোর করার চেষ্টা করবো। + +এই ক্লাসে একটা প্রশ্ন এসেছিলো আমরা Redis কখন ব্যবহার করবো? আমরা মূলত Redis ক্যাশিং করার কাজে ব্যবহার করবো। ধরেন আপনার অনলাইনে মোবাইলের ওয়েবসাইট আছে। এখন নতুন কোনো মোবাইল আসলো। সে মোবাইলের ইনফরমেশনের জন্য ১ লক্ষ মানুষ রিকোয়েস্ট দিলো। এখন এই ১ লক্ষ রিকোয়েস্ট প্রতিবার ডাটাবেজ থেকে প্রসেসিং করে আনতে অনেক খরচ পড়ে যায়। আমরা সেক্ষেত্রে রেডিস ব্যবহার করে প্রথমবার সে রিকোয়েস্ট ডাটাবেজ থেকে প্রসেসিং করে ইন মেমোরিতে ক্যাশিং করে রেখে দিবো। মোবাইল তো আর প্রতিদিন আসে না। ধরেন আমি ২৪ ঘন্টার টাইম লিমিট দিয়ে দিলাম, ডাটা ২৪ ঘন্টা আপডেট হবে না। ২৪ ঘন্টা পর অটোমেটিক ডাটা মেমোরি থেকে হারিয়ে যাবে, আবার নতুন করে ডাটা ডাটাবেজ থেকে প্রসেস করে আনবে। এই ২৪ ঘন্টার মধ্যে ১০ লক্ষ রিকোয়েস্ট আসলে তা ঐ ক্যাশিং এর ডাটা থেকে পেয়ে যাবে। এতে আমাদের ডাটাবেজের খরচও কমে যাবে। আবার যদি কেউ কোনো প্রোডাক্ট অর্ডার করে তাহলে প্রথমে কার্টে অ্যাড হয়, এরপর ইনভয়েস জেনারেট হয়, তারপর ইউজারের কাছে ইনভয়েস নামক একটা মেইল যাবে, তারপর ওয়্যারহাউজে একটা নোটিফিকেশন যাবে সেটা প্রসেসিং করার জন্য। অনেকগুলো স্টেপ। এখন সব স্টেপ কমপ্লিট করে এসে যদি ইউজারকে দেখানো হয় 'Order Accepted' সেটা অনেক সময়ের ব্যাপার। সে সময়টা ইউজার কিছুই করতে পারছে না অপেক্ষা করা ছাড়া। এটাতে ইউজার এক্সপেরিয়েন্স ভাল হয় না। এক্ষেত্রে আমরা রেডিস ব্যবহার করতে পারি। একটা ইভেন্ট ক্রিয়েট করবে অর্ডার রিকোয়েস্ট আসলে। সেই ইভেন্ট কিউ (Queue) তে রেখে আমরা প্রথমে ইউজারকে ম্যাসেজ দিয়ে দিবো। এরপর আমাদের বাকি কাজ একে একে আমরা সারবো। এতে ইউজার সাথে সাথে ফিডব্যাক পেয়ে গেলো। + +এই দুনিয়াতে মারামারি চলে SQL আর NoSQL এর মধ্যে। এখন উপরের প্যারাডাইমের মধ্যে কোনটা SQL আর কোনটা NoSQL তা একটু জেনে নিই। শুধুমাত্র Relational Database হলো SQL ডাটাবেজ, আর বাকি সব NoSQL. + +এখন আরেকটা প্রশ্ন আসতে পারে এখানে অনেক ডাটাবেইজই তো MongoDB আসার আগে এসেছে তাহলে কেন আগে NoSQL টার্মটা শোনা যায়নি। শোনা যায়নি কারণ এই টার্মটা প্রথম MngoDB ব্যবহার করেছে। তাই আগে শোনা যায়নি। + +SQL আর NoSQL এর মধ্যে কিছু ভুল ধারণা আছে। সেগুলো নিচে দেয়া হলো। + +প্রথম ধারণা হলো আমরা SQL শিখে যখন NoSQL এ যাই, তখন সিক্যুয়েলের কনসেপ্টই নো সিক্যুয়েলে লাগাতে চাই। কিন্তু সেটা তো সম্ভব হয় না, কারণ দুইটার কনসেপ্ট পুরোপুরি ভিন্ন। তখন ঐ ডেভেলপারদের কাছে নো সিক্যুয়েল খারাপ হয়ে যায়। + +দ্বিতীয় ধারণা হলো স্ট্যাক। প্রতিটা টেকনোলজির সাথে এক একটা স্ট্যাক ক্রিয়েট হয়ে যায়। যেমন পিএইচপির সাথে MySQL, জ্যাঙ্গোর সাথে PostgreSQL, nodejs এর সাথে MongoDB এরকম। এই কারণে আমরা ধরে নিই যে এই টেকনোলজির সাথে এই ডাটাবেজ ছাড়া অন্য কোনো ডাটাবেজ যাবে না। কিন্তু এটা সম্পূর্ণ ভুল ধারণা। যেকোনো ল্যাঙ্গুয়েজের সাথে যেকোনো ডাটাবেজ ফিট করা যায়। + +এরপর আরেকটা ভুল ধারণা হলো MongoDB তে ট্রানজেকশন নিয়ে কাজ করা যায় না। + +এরপর হলো MongoDB তে joining করা যায় না। এটা সত্যি কথা, কারণ যেখানে টেবিল নেই সেখানে জয়েন কিভাবে করবো। তবে এর মতো একই টেকনিক আছে। যেমন Aggregate, lookup এরকম কিছু অ্যাডভান্সড কনসেপ্ট দিয়ে আমরা টেবিল জয়েনিং এর চেয়েও বেটার কাজ করে ফেলতে পারি। আবার এমন কিছু কনসেপ্ট রয়েছে যেগুলো খুব হাই লেভেল। যেমন টাইম সিরিজ ডাটা। ধরেন আপনি একটা সিস্টেম ডিজাইন করছেন যেখানে রুগীর ব্লাড প্রেশার মনিটর হবে। প্রতি মিনিটে। তাহলে প্রতি মিনিটের ডাটা মিনিট বাই মিনিট নিতে নিতে একটা টাইম সিরিজ হয়ে গেলো। এই টাইম সিরিজ ডাটা স্টোর করা এবং কুয়েরি করে বের করে আনা অনেক কঠিন। সেই জায়গায় MongoDB অনেক সহজ একটা সার্ভিস প্রোভাইড করে। + +এবার আসি আমাদের মূল আলোচনা MongoDB নিয়ে। + +এর ওয়েবসাইটে গেলে আমরা দেখবো এটা এখন ডাটাবেজের চেয়েও বেশি। এর চারটা প্রোডাক্ট রয়েছে। Atlas, Enterprise Advanced, Community Edition, Realm। আমরা সাধারণত Community Edition নিয়ে কাজ করবো। এটা ফ্রি। এটা আমাদের মেশিনে ইনস্টল করে আমরা কাজ করতে পারবো। Enterprise Advanced ও Community Edition এর মতোই। তবে এখানে আমরা এক্সট্রা সাপোর্ট পাবো। কোথাও কোনো সমস্যায় পড়লে ওদেরকে পে করলে আমরা সেই সম্পর্কে সাপোর্ট পাবো। Atlas হচ্ছে মঙ্গোডিবির ম্যানেজড সার্ভিস। ম্যানেজড সার্ভিস হলো এই ডাটাবেজ হোস্ট করা, ম্যানেজ করা, ডেপ্লয় করা, প্যাচ করা, সিকিউরিটি প্রোভাইড করা এর কোনো কাজ আপনাকে করতে হবে না। আপনার কাজ হচ্ছে আপনি ডাটাবেজ ক্রিয়েট করে যেভাবে কাজ করেন সেভাবে কাজ করা। বাকি কাজ এই কোম্পানি আপনার জন্য করে দিবে। Atlas ব্যবহার করলে মঙ্গোডিবির লেটেস্ট ভার্সন আপনি পাবেন, ডাটা ম্যানেজ করার দায়িত্ব এদের, স্কেল করার দায়িত্ব এদের, সিকিউরিটি লিক হলে তা ফিক্স করার দায়িত্ব এদের, ডাটা ব্যাকআপ করার দায়িত্ব এদের, নতুন কোনো আপডেট আসলে আপডেট দিয়ে দেয়ার দায়িত্ব এদের আপনার কোনো দায়িত্ব নেই। আপনার কাজ হলো একটা ডাটাবেজ ক্রিয়েট করে তা ম্যানেজ করা। এটাকেই বলে ম্যানেজড সার্ভিস। অবশ্যই ম্যানেজড সার্ভিস নিতে গেলে আপনাকে পে করতে হবে। কম্যুনিটি এডিশনে এতক্ষণ যা যা বললাম সব আপনার নিজেকেই ম্যানেজ করতে হবে। আরেকটা প্রোডাক্ট আছে তার নাম Realm। এটা মঙ্গোডিবির সার্ভারলেস টেকনোলজি। সার্ভারলেস হচ্ছে আমাদের ভবিষ্যত। ধরেন কোনো ইউজার তার আইডি ডিলিট করতে চাইছে। এখন তার সমস্ত ডাটা ডিলিট করার পরই আমরা তার আইডি ডিলিট করতে পারবো। এখন ডিলিট একটা কমপ্লেক্স টাস্ক। ইউজার যখন ডিলিট রিকোয়েস্ট পাঠায় তখন আমাদেরকে তার পোস্ট ডিলিট করতে হবে, তার সমস্ত ছবি, ভিডিও, লাইক, কমেন্ট, শেয়ার, প্রোফাইল, ইনফরমেশন সব ডিলিট করতে হবে। এখন যদি কয়েক বছরের বিলিয়ন বিলিয়ন ডাটা জমে থাকে তাহলে সব ডিলিট করে এরপর ওকে বলতে পারবো তোমার সব ডাটা ডিলিট হয়ে গেছে। এটা করতে অনেক সময় লাগবে। ততক্ষণ পর্যন্ত কি ইউজারকে আমরা বসিয়ে রাখবো? দুইটা উপায় আছে। একটা হলো পূর্বের মতো ইভেন্ট ড্রিভেন আর্কিটেকচার। আমরা রিকোয়েস্ট কিউতে রেখে একটা ইভেন্ট তৈরি করে ম্যাসেজ দিলাম যে আমরা প্রসেস শুরু করেছি, প্রসেস শেষ হলে আমরা তোমাকে জানাবো। এরপর প্রসেস শেষে আমরা আরেকটা ইভেন্ট ক্রিয়েট করে তাকে মেইলের মাধ্যমে জানালাম প্রসেস শেষ। এটা সবচেয়ে সহজ উপায়। এছাড়া আরেকটা উপায় আছে। সেটা Realm আসার পর আমরা মঙ্গোডিবির ক্লাউড ফাংশনে গিয়ে একটা ফাংশন লিখে দিয়ে আসতে পারি যে যখন ডিলিট রিকুয়েস্ট আসবে তখনই এই ফাংশনটা ট্রিগার হবে। এবার ফাংশনের মধ্যে আমরা কোড লিখে দিলাম যে যখন ইউজার ডিলিট হবে তখন তার সম্পর্কিত সকল ডাটা ডিলিট হয়ে যাবে। এটা আসার পর আমাদের আর কিউ, ইভেন্ট, ম্যাসেজ এতকিছু ক্রিয়েট করার প্রয়োজন পড়ছে না। আমরা একটা ফাংশন লিখে দিলেই কাজ শেষ। আমাদের খরচ কমে যাবে। ফাংশন সার্ভিসের একটা সুবিধা হলো আপনি ফাংশন লিখে আসলেও সেটা চার্জ করবে না ততক্ষণ পর্যন্ত, যতক্ষণ পর্যন্ত এটা ট্রিগার হবে না। শুধু যতক্ষণ এই ফাংশন এক্সিকিউশন হবে ততক্ষণ পর্যন্তই আপনাকে এরা চার্জ করবে। তার মানে হলো এটা এক্সিকিউট হতে যদি ১০ মিলিসেকেন্ড নেয় তাহলে ১০ মিলিসেকেন্ডের চার্জ নিবে আর যদি ১০ সেকেন্ড নেয় তাহলে ১০ সেকেন্ডের চার্জ নিবে। আবার এই Realm সার্ভিসের আরেকটা সুবিধা হলো আপনি এটা ব্যবহার করে খুব সহজে GraphQL সার্ভার তৈরি করে ফেলতে পারবেন ব্যাকএন্ডে এক লাইন কোড লেখা ছাড়া। আপনি এখানে API তৈরি করতে পারেন যা আপনি যেকোনো ওয়েব বা মোবাইল অ্যাপ্লিকেশনের সাথে কানেক্ট করতে পারেন। কোনো ব্যাকএন্ড তৈরি না করে আপনার একটা ব্যাকএন্ড সার্ভিস তৈরি হয়ে যাবে অনেক ফায়ারবেসের মতো। কিন্তু ফায়ারবেসের চাইতে অনেকটা অ্যাডভান্সড। Realm FAAS (Function As A Service) এবং BAAS (Backend As A Service) সার্ভিস প্রোভাইড করে। ফায়ারবেস মূলত BAAS সার্ভিস প্রোভাইড করে তবে গুগল ক্লাউডের সাহায্য নিয়ে আমরা দুইটা সার্ভিসই পেতে পারি। বাজারে ছোটখাট ব্যাকএন্ড ডেভেলপারের এখন আর কোনো দরকার নাই। কারণ ব্যাকএন্ডের কাজগুলো এত রিপিটেটিভ যে খুব কমপ্লেক্স লেভেলের অ্যাপ্লিকেশন না হলে ঐ দুই ধরণের সার্ভিস ব্যবহার করে তা করে ফেলা যায়। এর জন্য বাজারে firebase, supabase, MongoDB Realm, Amazon Amplify, Amazon Lambda এই ধরণের প্লাটফর্ম রয়েছে। + +মঙ্গোডিবিতে আপনি ডাটা নিয়ে কাজ করছেন মানে আপনাকে স্কেলিং নিয়ে ভাবতে হবে না। ইউজার যতো হবে সে সেই লেভেলের স্কেলিং করার ক্ষমতা রাখে। এটা ডিজাইনই করার হয়েছে ট্রিলিয়ন ট্রিলিয়ন ডাটা নিয়ে কাজ করার জন্য। এত পরিমাণ ডাটা থেকে খুব কম সময়ে ডাটা কুয়েরি করে নিয়ে আসতে পারে। + +মঙ্গোডিবি হলো ডকুমেন্ট ডাটাবেজ। এখন এই ডকুমেন্ট ডাটাবেজ আমাদের কি ধরণের হেল্প করে? বর্তমানে সব বড় বড় কোম্পানি টিকে রয়েছে ডাটার উপরে। বিভিন্ন র‍্যান্ডম ডাটার উপরে। ডাটার উপর ভিত্তি করেই দুনিয়াটা চলছে। র‍্যান্ডম ডাটাকে কোনো একটা স্কিমার মধ্যে ফেলা খুব কঠিন। এই র‍্যান্ডম ডাটাকে আপনি কোনো রিলেশনাল ডাটাবেজের মডেলে ফেলতে পারবেন না। কারণ রিলেশনাল ডাটাবেজে ফেলতে হলে আপনার আগে থেকে একটা স্কিমা থাকতে হবে বা ডাটা মডেল থাকতে হবে। তাহলে আমরা এই র‍্যান্ডম ডাটাগুলোকে কিভাবে ম্যানেজ করতে পারবো? মঙ্গোডিবির মাধ্যমে। তাহলে প্রশ্ন আসতে পারে মঙ্গোডিবির আগে কি এসব ডাটা প্রসেস করা হতো না? অবশ্যই হতো। প্রথমে ডাটাকে বিভিন্ন টেক্সট ফাইলে রাখতে হতো, এরপর সেখানে থেকে প্রসেস করে বিভিন্ন শেইপে ফেলে তারপর কাজ করা হতো। মঙ্গোডিবি এসব আনশেইপড র‍্যান্ডম ডাটা নিয়ে কাজ করার প্রব্লেম সলভ করার জন্য এসেছে। কারণ ডাটা র‍্যান্ডমলি চেইঞ্জ হওয়া মানে স্কিমা চেইঞ্জ হওয়া। আর রিলেশনাল ডাটাবেজে বারবার স্কিমা পরিবর্তন করা অনেক কঠিন কাজ যেটা মঙ্গোডিবি অনেক সহজে করতে পারে। ডকুমেন্ট ডাটাবেজে একটা কালেকশনে একরকমের ডাটা থাকতে পারে অন্যটাতে আরেক রকমের। এটাই হচ্ছে সুবিধা নো সিক্যুয়েল ডাটাবেজের। এখানে ডাটার কোনো শেইপ নেই। র‍্যান্ডম যেকোনো টাইপের ডাটা আপনি আপনার কালেকশনের মধ্যে সেভ করতে পারবেন। + +আরেকটা কারণ আছে। সেটা হলো জয়েনিং। যখন ডাটার শেইপের প্রশ্ন আসে, সিক্যুয়েল ডাটাবেজে একটা কনসেপ্ট আসে সেটা হলো ডাটা নরমালাইজেশন। যেখানে আমাদের একটা টেবিলকে ভেঙে আরেকটা টেবিল বানানো যায় সেখানে আমরা আরেকটা টেবিল বানাবো, এরপর একট পিভট টেবিল বানাবো। বানিয়ে দুইটাকে কানেক্ট করে দিবো। ধরেন আমরা কিছু ছাত্রের ইনফরমেশনের জন্য একটা টেবিল বানালাম। তার নাম, ফোন নাম্বার, এডুকেশন, এড্রেস। এখন সে ধরেন একাধিক ইনস্টিটিউশনে পড়ালেখা করেছে তাহলে এই টেবিল থেকে ব্রেক করে আমরা এডুকেশনের জন্য আলাদা টেবিল বানাতে পারি। আবার তার কয়েকটা অ্যাড্রেস থাকতে পারে, তাহলে সেখান থেকে ব্রেক করে আমরা অ্যাড্রেসের জন্য আলাদা টেবিল বানিয়ে ফেলতে পারি। এভাবে যেখানে দরকার আমরা সব ইনফরমেশন একসাথে না রেখে আলাদা আলাদা টেবিলে ব্রেক করে ডাটাকে নরমালাইজ করে নিতে পারি। এই নরমালাইজেশনের সুবিধাও আছে অসুবিধাও আছে। যেমন আমরা আমাদের সুবিধামতো টেবিলে ভাগ করে নিতে পারছি। একটা টেবিলে সব ইনফরমেশন থাকছে না। ডাটা ম্যানেজ করা অনেক সহজ হয়ে যাচ্ছে। কিন্তু যখনই আমরা সব টেবিলের ডাটা একসাথে পেতে চাইছি তখনই তা প্রতিটা টেবিলকে জয়েন করার মাধ্যমে এই ডাটা দিতে পারবে। এই জয়েনিং এর প্রসেসটা অনেক ব্যয়বহুল। নিচের ছবিটা একটু দেখি আমরা। + +![Data Duplication](./images/data-duplication.png) + +এখানে দেখুন নাম, ইমেইল আর পাসওয়ার্ড একই, বাকিগুলো ভিন্ন। সেক্ষেত্রে নাম, ইমেইল এবং পাসওয়ার্ডের ক্ষেত্রে ডাটা ডুপ্লিকেশন হচ্ছে। এখন আমরা করবো কি যেগুলোতে ডাটা ডুপ্লিকেশন হচ্ছে সেগুলো এতবার না লিখে একবার লিখবো। + +![dd](./images/Screenshot_2.png) + +আর যেগুলো ভিন্ন সেগুলোর জন্য আলাদা টেবিল বানাবো। এটাকেই বলা হয় ডাটা নরমালাইজেশন। + +![dd](./images/Screenshot_1.png) + +এখানে যে সমস্যাটা হয় এখন আমি যদি জানতে চাই এই ইউজার মাস্টার্স কোথা থেকে করেছেন তাহলে দুইটা টেবিলকে জয়ন করে তারপর ডাটাটা বের করে আনতে হবে। এটা একটা বিশাল সমস্যা। এই সমস্যার সমাধান করা যায় একটা সিম্পল JSON অবজেক্টের মাধ্যমে এভাবে। + +```json +{ + "ID": "12345", + "name": "Alvi", + "email": "alvi@gmail.com", + "password": "1234", + "educations": [ + { + "1": 2, + "2010": 2014, + "HSC": "BSC", + "College": "Dhaka University", + "1__1": 1, + }, + { + "1": 3, + "2010": 2016, + "HSC": "MSC", + "College": "BUET", + "1__1": 1, + }, + ], +} I +``` + +কোনো টেবিল নাই, কলাম নাই, রো নাই। যা খুশি ডাটা রাখতে পারছি। যেভাবে খুশি রাখতে পারছি, যতো চাই ততো পারছি। এখন ধরেন কারো কোনো এডুকেশনাল ব্যাকগ্রাউন্ড নাই তার জন্য আপনি ডাটা রাখতে চাইছেন। সেটাও পারবেন। এডুকেশন না দিলেও কোনো ক্ষতি হবে না। + +আজকের ক্লাসে আমরা ডাটাবেজ নিয়ে একটা বেসিক ধারনা দেয়ার চেষ্টা করলাম। ডাটাবেজ সম্পর্কে যত ধরণের ভুল ধারণা আছে সব আশা করি আপনাদের মিটে গিয়েছে। SQL, NoSQL নিয়ে মারামারির দরকার নাই। আমার অ্যাপ্লিকেশনের জন্য আমার যে ডাটাবেজ দরকার আমরা সেটাই ব্যবহার করবো। কোনোটাই খারাপ না। সবাই যার যার উদ্দেশ্য পূরণের ক্ষেত্রে সফল। + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/full-stack-army/class-overview/Lecture-18/images/Screenshot_1.png b/full-stack-army/class-overview/Lecture-18/images/Screenshot_1.png new file mode 100644 index 0000000..35dab8b Binary files /dev/null and b/full-stack-army/class-overview/Lecture-18/images/Screenshot_1.png differ diff --git a/full-stack-army/class-overview/Lecture-18/images/Screenshot_2.png b/full-stack-army/class-overview/Lecture-18/images/Screenshot_2.png new file mode 100644 index 0000000..2d0c881 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-18/images/Screenshot_2.png differ diff --git a/full-stack-army/class-overview/Lecture-18/images/data-duplication.png b/full-stack-army/class-overview/Lecture-18/images/data-duplication.png new file mode 100644 index 0000000..a159d27 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-18/images/data-duplication.png differ diff --git a/full-stack-army/class-overview/Lecture-18/images/document-oriented.png b/full-stack-army/class-overview/Lecture-18/images/document-oriented.png new file mode 100644 index 0000000..66ffb5f Binary files /dev/null and b/full-stack-army/class-overview/Lecture-18/images/document-oriented.png differ diff --git a/full-stack-army/class-overview/Lecture-18/images/key_value.png b/full-stack-army/class-overview/Lecture-18/images/key_value.png new file mode 100644 index 0000000..34fba6c Binary files /dev/null and b/full-stack-army/class-overview/Lecture-18/images/key_value.png differ diff --git a/full-stack-army/class-overview/Lecture-18/images/wide_column.png b/full-stack-army/class-overview/Lecture-18/images/wide_column.png new file mode 100644 index 0000000..ff909c2 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-18/images/wide_column.png differ diff --git a/full-stack-army/class-overview/Lecture-19/README.md b/full-stack-army/class-overview/Lecture-19/README.md new file mode 100644 index 0000000..ebc7c16 --- /dev/null +++ b/full-stack-army/class-overview/Lecture-19/README.md @@ -0,0 +1,3 @@ +# Lecture 19 - Backend 6 | Adda with Random Topics | You can Skip + +এই ক্লাসে কিছু র‍্যান্ডম টপিক নিয়ে আলোচনা হয়েছে। সেরকম কোনো বিষয়বস্তু ছিল না। আপনারা চাইলে এটার ভিডিও দেখতে পারেন অথবা স্কিপ করতে পারেন। diff --git a/full-stack-army/class-overview/Lecture-20/README.md b/full-stack-army/class-overview/Lecture-20/README.md new file mode 100644 index 0000000..9219f56 --- /dev/null +++ b/full-stack-army/class-overview/Lecture-20/README.md @@ -0,0 +1,449 @@ +# Lecture 20 - Backend 7 | Start Working with Mongoose + +এর আগের ক্লাসে আমরা ডাটাবেজ নিয়ে একটা ওভারভিউ দিয়েছিলাম। আর বলেছিলাম আমরা প্রথমে মঙ্গোডিব শিখবো। তার জন্য লেকচার ১৭ তে একটা টাস্কও দেয়া হয়েছিল মঙ্গোডিবির উপর একটা ধারণা নেয়ার জন্য। আমরা ডেভেলপাররা কেন জানিনা ডাটাবেজকে একটু এড়িয়ে চলতে চাই। আমরা আমাদের প্রজেক্টে raw মঙ্গোডিবি কোড লিখিনা। আমরা ব্যবহার করি ORM (Object Relational Mapper)। এই ORM সব ধরণের ফ্রেমওয়ার্কেই আছে। যারা PHP/LARAVEl নিয়ে কাজ করেন তাদের জন্য আছে Eloquent নামে একটা ORM, জাভাতে আছে Hibernate। তো এরকম প্রতিটা ফেমওয়ার্কে কিছু না কিছু উপায় থাকে যে উপায় ব্যবহার করে আপনি খুব সহজেই ডাটাবেজের সাথে কমিউনিকেট করতে পারবেন প্রোগ্রামারের ওয়েতে। প্রোগ্রামারের ওয়েতে মানে কিরকম? প্রোগ্রামাররা কুয়েরি লিখতে পছন্দ করেন না। কারণ এটা আলাদা একটা কনসেপ্ট, আলাদাভাবে শিখতে হয়। এত ঝামেলায় না গিয়ে আমরা ORM ব্যবহার করি। এতে আমরা ফাংশন বা ক্লাস কল করার মাধ্যমে ডাটাবেজ রিলেটেড কাজ করতে পারি। তার মানে কি আমরা ডাটাবেজ না শিখেই ডাটাবেজের গভীরের কাজগুলো করতে পারবো? কখনোই না। কিন্তু ডেইলি বেসিসে যে বেসিক কাজগুলো আছে সেগুলো করতে পারবো। যেহেতু আমরা এখনও বিগিনার আমাদের ডাটাবেজের অতো গভীরে যাওয়ার কোনো প্রয়োজন নেই। আমরা আপাতত Mongoose নিয়ে কাজ করবো। + +Mongoose নিয়ে কাজ করতে গেলে আমাদের মেশিনে মঙ্গোডিবি কানেক্টেড থাকতে হবে। আমরা অনেকভাবেই মঙ্গোডিবি কানেক্ট করতে পারি। আমরা [Compass](https://www.mongodb.com/products/compass) ডাউনলোড করে ইনস্টল করে কানেক্ট করতে পারি। আমরা [MongoDB Installation](https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-windows/) ফলো করে মঙ্গোডিবি ইনস্টল করে কানেক্ট করতে পারি। আমরা [Docker](https://www.docker.com/) ব্যবহার করে কানেক্ট করতে পারি। যেকোনো ভাবে আমরা কানেক্ট করতে পারি। ভিএস কোডে MongoDB নামে একটা এক্সটেনশন আছে। সেটা ব্যবহার করে আমাদের ডাটাগুলো আমরা দেখতে পারি। এখানে docker কনসেপ্টটা একটু নতুন। এটা নিয়ে পরের বিস্তারিত আলোচনা করা হবে। আপাতত সামান্য জেনে রাখুন। ধরুন আপনি একটা অ্যাপ বানালেন উইন্ডোজে। আমাকে দিলেন সেটা। আমি ব্যবহার করি লিনাক্স। এখন সেটা আমার মেশিনে কাজ করছে না। এই সমস্যার সল্যুশন হলো। আপনি ডকারে এই অ্যাপ্লিকেশনের ইমেজ ক্রিয়েট করে সেটা আমাকে দিবেন। আমি ডকারে সেটা রান করবো। ডকার থাকলে কোন মেশিন সেটা কোনো সমস্যা না। যেকোনো মেশিনের কোড ভিন্ন মেশিনে রান করানো যাবে। + +কম্পাস ব্যবহার করে কিভাবে কানেক্ট করবেন একটু দেখাচ্ছি। প্রথমে কম্পাস ইনস্টল করে নিবেন। এরপর রান করবেন। রান করলে নিচের ছবির মতো স্ক্রিন পাবেন। + +![mongodb](./images/mongodb-1.png) + +URI এ `mongodb://localhost:27017` এটা লিখে কানেক্ট বাটনে প্রেস করবেন। আপনার ডাটাবেজ কানেক্ট হয়ে যাবে। + +এবার দেখাই কিভাবে কমান্ড লাইন ব্যবহার করে কানেক্ট করবেন। [MongoDB Installation](https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-windows/) ফলো করে যেভাবে যা যা বলেছে সব কাজ করুন। করার পর পাওয়ারশেল বা আপনার কমান্ড লাইন ওপেন করুন। করার পর `mongod` লিখে এন্টার করুন। আপনার ডাটাবেজ কানেক্ট হয়ে যাবে। + +Mongoose নিয়ে কাজ করতে হলে আমাদের প্রথমে Mongoose ইনস্টল করে নিতে হবে `yarn add mongoose` লিখে। এবার আমরা mongoose কিভাবে কাজ করে তা দেখবো। + +প্রথমে আমাদের জানতে হবে ডাটাবেজের কাজ কি? একদম বেসিক কাজ হলো CRUD (Create, Read, Update, Delete) করা। এখন এগুলো আমরা কিভাবে করতে পারি? ডাটার স্কিমা (Schema) তৈরি করে। এখন মঙ্গোডিবিতে আবার স্কিমা কেন? মঙ্গোডিবি তো স্কিমালেস। তাহলে কেন আমরা এখানে স্কিমা বানাবো। কিছু পয়েন্ট আছে। চলুন সেগুলো আলোচনা করি। + +- আমরা ডাটা রাখবো ডাটাবেজে। এখন ডাটা রাখা অনেক ব্যয়বহুল। এখন যদি আমরা ডাটা ভ্যালিডেশন ছাড়া জাংক ডাটা রাখি সেক্ষেত্রে তো আমাদের খরচ বেড়ে যাবে অকারণে। আমি কেন কিছু জাংক ডাটার জন্য এত খরচ করবো। আমি সেগুলোই রাখতে দিবে যেগুলো অর্থবহ ডাটা। এই ডাটা ভ্যালিডেশনের জন্য আমরা স্কিমা বানাই। +- দ্বিতীয় পয়েন্ট হলো আমরা ডেভেলপাররা বুঝি আমাদের কতটুকু পর্যন্ত স্বাধীনতা প্রয়োজন। যেমন জাভাস্ক্রিপ্টে ডাটা টাইপ ডিফাইন করার কোনো সিস্টেম নেই। আমরা যেকোনো ডাটা নিয়ে কাজ করতে করতে একসময় কখন যে আমাদের অ্যাপ্লিকেশন ফেইলিওর হয়ে যাবে সেটা আমরা বুঝতে পারবো না। কারণ আমি ভুলভাল ডাটা নিয়ে কাজ করতে পারি। এজন্য টাইপস্ক্রিপ্ট আমাদের পছন্দ। কারণ সে কিছু সীমাবদ্ধতা আমাদের জন্য রেখে দিয়েছে। যার কারণে আমরা ভুলভাল ডাটা নিয়ে কাজ করতে গেলে সে আমাদের তা করতে দিবে না। সেরকম মঙ্গোডিবি যখন আস্তে আস্তে স্ট্রাকচারড ডাটার জন্য ব্যবহার শুরু হলো, তখন তার জন্য একটা স্ট্রাকচার দরকার ছিল, ডাটাসমূহের রিলেশনশীপের জন্য একটা শেইপ বা মডেল দরকার হলো, ভ্যালিডেশনের জন্য একটা সিস্টেম দরকার হলো। এই সবকিছু আমাদের প্রোভাইড করছে Mongoose। Mongoose এর মাধ্যমে আমরা স্কিমা তৈরি করতে পারি, আর Mongoose আমাদের হয়ে ডাটাবেজের সাথে কমিউনিকেট করছে। + +স্কিমার আরেকটা সুন্দর নাম আছে। সেটা হলো Entity। যখন আপনি SQL ডাটাবেজ নিয়ে কোনো কোর্স করবেন বা কাজ করবেন তখন একটা কনসেপ্ট আপনার সামনে আসবে। সেটা হলো Entity Diagram। যা দেখতে অনেকটা নিচের ছবির মতো। + +![Entity Diagram](./images/entity-diagram.png) + +এখানে উপরে হাইলাইট করে টেবিলের নাম আছে। যখন আমরা SQL নিয়ে কাজ করবো তখন এটা টেবিল আর যখন NoSQL নিয়ে কাজ করবো তখন তা হলো কালেকশন। তারপর SQL Database এ প্রাইমারি কী (PK) দরকার হয়, কিন্তু NoSQL ডাটাবেজে প্রাইমারি কী প্রয়োজন হয় না। এরপর আছে আমাদের প্রোপার্টিগুলো। এখানে কোনো ডাটা টাইপ বলে দেয়া হয়নি। যদি আপনি চান তাহলে ডাটা টাইপও বলে দিতে পারেন। এরপর দেখবেন অনেক ধরণের কানেক্টিং লাইন দেখা যাচ্ছে। এই লাইনগুলোর একেকটার মিনিং একেকরকম। কোনোটা One to One relationship বুঝায়, কোনোটা One to many relationship বুঝায়, কোনোটা আবার Many to Many relationship বুঝায়। এই রিলেশনশীপগুলো একট্য বুঝার চেষ্টা করি। + +- One to One Relationship - ধরেন একজন ইউজারের একই সময় একটাই প্রোফাইল থাকবে। দুইটা কখনই হতে পারবে না। আবার ঐ প্রোফাই্লের মালিক একজন ইউজারই হতে পারবে, দুইজন কখনই হতে পারবে না। এখানে যদি আমরা ইউজারকে একটা স্কিমা আর প্রোফাইলে আরেকটা স্কিমা ধরি তাহলে এটা হচ্ছে One to One Relationship। +- One to many relationship - ধরেন ইউজার পোস্ট করেন। এখন একজন ইউজারের অনেকগুলো পোস্ট থাকতে পারে। সে দিনে ১০টা পোস্টও করতে পারে। তার মানে একজন ইউজারের অনেক পোস্ট। কিন্তু সব পোস্টের মালিক ঐ একজন ইউজারই। দুইজন হতে পারবে না। এটাকে বলে One to many relationship. +- Many to Many relationship - ধরেন ইউডেমি বা অন্য কোনো সাইটে একটা কোর্স ৩ জন মিলেও বানাতে পারে। আবার ঐ ৩ জন আলাদা আলাদা ভাবে আরো কোর্স বানাতে পারে। তার মানে একটা কোর্সের মালিক ৩ জন হতে পারে, আবার ঐ ৩ জনের মাল্টিপল কোর্স থাকতে পারে। এটাই হচ্ছে Many to Many relationship। + +এবার আমরা Mongoose নিয়ে একটু কাজ করবো। তার জন্য আমরা একটা জাভাস্ক্রিপ্ট ফাইল ক্রিয়েট করবো। [Mongoose](https://mongoosejs.com/docs/guide.html) এর ডকুমেন্টেশন অনেক বড়। এটা যদি আমরা মোটামুটি বুঝতে পারি তাহলে ডাটাবেজ নিয়ে কাজ করা আমাদের জন্য অনেক সহজ হয়ে যাবে। আমরা প্রথমে আমাদের ফাইলে Mongoose ইমপোর্ট করে নিবো। এবং কানেক্ট করবো ডকুমেন্টেশন অনুযায়ী। + +```js +// index.js + +const mongoose = require('mongoose'); + +mongoose.connect('mongodb://localhost:27017/mongo-demo'); +``` + +এখানে `mongo-demo` আমাদের ডাটাবেজের নাম। আপনারা এর পরিবর্তে যা খুশি দিতে পারেন। ডকুমেন্টেশনে এতটুকুতে শেষ করে দিয়েছে। কিন্তু আমরা আরেকতু গভীরে যাবো। এই ফাংশনটা একটা প্রমিজ রিটার্ন করে। যেহেতু প্রমিজ রিটার্ন করে সেহেতু আমরা then catch ব্লক ইউজ করতে পারি। + +```js +// index.js + +const mongoose = require('mongoose'); + +mongoose + .connect('mongodb://localhost:27017/mongo-demo') + .then(async () => { + console.log('Database connected'); + }) + .catch((e) => { + console.log(e); + }); +``` + +আমরা এখানে দিলাম যখন ডাটাবেজ কানেক্টেড হবে তখন আমাদের একটা ম্যাসেজ দিবে। যদি এরর হয় তার জন্য আমরা ক্যাচ ব্লক লিখলাম। এবার একটু রান করে দেখি আমাদের ডাটাবেজ কানেক্ট হয় কিনা। যদি রান করার পর 'Database connected' লেখা আসে তাহলে ধরে নিবেন আপনার ডাটাবেজ কানেক্ট হয়েছে। যদি কম্পাসে গিয়ে দেখি দেখবো কোনোকিছু নেই mongo-demo নামে। কারণ ডাটাবেজ কানেক্ট হয়ে যে বসে আছে, সেটা ক্লোজ করিনি। ক্লোজ না করলে তা কানেক্ট হয়ে বসে থাকবে। আর কিছুই করবে না। তাই আমাদের ক্লোজ করতে হবে কানেক্ট হওয়ার পর। যদিও এরপরও দেখা যাবে না কারণ কোনো ডাটা এখনও আমরা ক্রিয়েট করিনি। ক্রিয়েট করলে দেখা যাবে। + +```js +const mongoose = require('mongoose'); + +mongoose + .connect('mongodb://localhost:27017/mongo-demo') + .then(async () => { + console.log('Database connected'); + }) + .catch((e) => { + console.log(e); + }) + .finally(() => { + mongoose.connection.close(); + }); +``` + +ডাটাবেজ কানেকশনের পর আমরা then ব্লকের ভিতর আমাদের যাবতীয় কাজসমূহ করবো। + +এখন যদি আমাদের ডাটাবেজে কোনো এরর ঘটে, ধরেন আমরা কানেকশন স্ট্রিং এ কিছু ভুল করলাম লিখতে, তাহলে Mongoose নিজস্ব টাইমআউট শেষ না হওয়া পর্যন্ত কানেক্ট হওয়ার চেষ্টা করবে, এরপর এরর দিবে। এখন আমরা চাইছি যদি ভুল হয় তাহলে সাথে সাথে যেন এরর দেয়। এটার জন্য Mongoose এ একটা সিস্টেম আছে। আমরা connect মেথডে আর্গুমেন্ট হিসেবে এই টাইমআউট সেট করে দিতে পারি। + +```js +const mongoose = require('mongoose'); + +mongoose + .connect('mongodb://loclhost:27017/mongo-demo', { + serverSelectionTimeoutMS: 10, + }) + .then(async () => { + console.log('Database connected'); + }) + .catch((e) => { + console.log(e); + }) + .finally(() => { + mongoose.connection.close(); + }); +``` + +দেখবেন ইনস্ট্যান্ট একটা এরর দিয়ে দিবে। তবে এত কম টাইমআউট দেয়া ভাল না। কারণ সবার নেট স্পীড একই না। কারো একটু স্লো। এখন যদি স্লো নেটের কেউ আমাদের ডাটাবেজের সাথে কানেক্ট হতে চায় সেক্ষেত্রে অতক্ষণ সে অপেক্ষা না করে ১০ মিলিসেকেন্ড পরেই এরর দিয়ে দিবে। তাই এটা আমরা ব্যবহার করলেও একটা লজিক্যাল টাইমআউট দিয়ে দিবো যেন যা স্লো নেটে কাজ করে তারাও কানেক্ট হতে পারে। + +এবার আমরা একটু Mongoose এর অফিসিয়াল ডকুমেন্টেশনে যাবো। Mongoose নিয়ে কাজ করতে গেলে সবার প্রথমে আমাদের যে জিনিসটা জানতে হয় সেটা হলো স্কিমা। মডেল বানানোর জন্য অবশ্যই আমাদের একটা স্কিমা লাগবে। তার মানে Mongoose এ সবচেয়ে গুরুত্বপূর্ণ element হচ্ছে Schemas। এরপর আছে SchemaTypes। এটা স্কিমার সাথে রিলেটেড। Connections এই মুহূর্তে খুব গুরুত্বপুর্ণ না। তবে আমরা যখন প্রোডাকশনে যাবো তখন বিভিন্নভাবে এরর হ্যান্ডলিং করতে হবে। তখন আমাদের Connections এর প্রয়োজন হবে। এরপর আছে Models। স্কিমা থেকে তৈরি হয় মডেল। আমরা সরাসরি স্কিমা নিয়ে কাজ করি না। স্কিমা জাস্ট একটা ডেফিনেশন, আমার ডাটার চেহারা কেমন হবে সেটা। স্কিমা থেকে তৈরি করা হয় মডেল। আর সেই মডেল নিয়ে আমরা কাজ করে থাকি। MVC (Model View Controller) প্যাটার্নে যে মডেলের কথা বলা হয় এখানে সেই একই মডেলের কথা বলা হচ্ছে। এরপর আছে Documents. ডকুমেন্ট বলতে বুঝায় আমরা কুয়েরি করার পর যে জিনিসটা রিটার্ন পাই সেটাই হচ্ছে ডকুমেন্ট। বা নতুন কিছু ইনসার্ট করার পর যে ডাটা আমরা রিটার্ন পাচ্ছি সেটা হলো ডকুমেন্ট। মূলত একটা json অবজেক্ট, যার মধ্যে আমরা স্টোর করে রেখেছি আমাদের সমস্ত ইনফরমেশন। একটা ডকুমেন্টের মধ্যে সাবডকুমেন্ট থাকতে পারে। সেই সাবডকুমেন্ট নিয়ে প্রপার ওয়েতে, ফ্লেক্সিবলি কাজ করার সুযোগ আমাদের মঙ্গোডিবি দেয় না, সেই সুযোগ খুব সহজে আমাদের দেয় Mongoose। যখন আমরা কুয়েরি করবো তখন আমাদের ডকুমেন্ট আর সাবডকুমেন্টের কাজ আসবে। এরপর আছে Queries। এরপর আছে Validation। এটা আমাদের যখন আমরা ডাটা ইনসার্ট করতে চাই বা আপডেট করতে চাই তখন আমাদের এই ভ্যালিডেশনটা কাজে লাগে। যেমন আপনি চাইছেন যে আমার কোনো নাম empty string হওয়া যাবে না, কিন্তু ইউজার empty string দিয়ে বসে আছে। এখন সেটা তো আপনি ডাটাবেজে স্টোর করবেন না। এই জায়গাটাই কাজ করে ভ্যালিডেশন। তারপর আছে মিডলওয়্যার। অনেকটা এক্সপ্রেস মিডলওয়্যারের মতোই। সার্ভার থেকে ডাটা ডাটাবেজে পাঠানোর পর ডাটাবেজে সেভ হওয়ার আগে আমরা সেই ডাটা ট্রান্সফর্ম করতে পারি, বা ডিলিট করতে পারি। সেই কাজটা করে এই মিডলওয়্যার। এরপর সবচেয়ে গুরুত্বপূর্ণ সিস্টেম হচ্ছে Populate। রিলেশনশীপ নিয়ে কাজ করতে চাইলে আমাদের এই পপুলেট নিয়ে কাজ করতে হবে। Discriminators অতো গুরুত্বপুর্ণ না। Plugins কিছু কিছু ক্ষেত্রে গুরুত্বপূর্ণ। আমরা বিভিন্ন কাস্টম প্লাগিন বানিয়ে কাজ করতে পারি। Transactions অ্যাডভান্সড কনসেপ্ট, সেটা নিয়ে আমরা পরে জানবো। ধরেন আপনি এটিএম বুথ থেকে টাকা তুলতে গেলেন। আপনি কার্ড ঢুকিয়ে পাসওয়ার্ড দিয়ে, অ্যামাউন্ট সিলেক্ট করলেন। আপনার টাকা কেটে ফেললো। এখন টাকা বের হওয়ার সময় কোনো কারণে টাকা আটকে গেলো, কিন্তু আপনার টাকা কাটা হয়ে গেছে। এক্ষেত্রে দুইটা উপায় আছে। হয় টাকা হাতে পাওয়ার পর টাকা কাটবে। আর যদি টাকা কেটেও ফেলে, তা কোনো কারণে আটকে গেলে সেটা আবার রিভার্স হয়ে যাবে। দুইটা অপারেশন যারা একে অন্যের উপর নির্ভরশীল, দুইটাই সাক্সেসফুল হলে তা সাক্সেস রিটার্ন করবে, নাহয় রোলব্যাক করবে, এরকম কাজ করার জন্য Transaction ব্যবহার করা হয়। + +আমরা স্কিমাটাইপস দিয়ে শুরু করি। আগে আমাদের জানতে হবে কোন কোন ডাটা টাইপের ডাটা ইউজ করা যাবে। তা নিচে দেয়া হলো। + +![SchemaTypes](./images/schematypes.png) + +কিভাবে স্কিমা টাইপ লিখতে হবে তার একটা ফরমেট ডকুমেন্টেশনে দেয়া আছে, যার স্ন্যাপশট দেয়া হলো। + +![example](./images/example.png) + +এবার আমাদের ফাইলে আমরা একটা স্কিমা বানাবো। আমরা একজন মানুষের জন্য স্কিমা বানাবো। আমরা লিখবো এভাবে + +```js +const mongoose = require('mongoose'); + +const personSchema = new mongoose.Schema({}); + +mongoose + .connect('mongodb://loclhost:27017/mongo-demo', { + serverSelectionTimeoutMS: 10, + }) + .then(async () => { + console.log('Database connected'); + }) + .catch((e) => { + console.log(e); + }) + .finally(() => { + mongoose.connection.close(); + }); +``` + +mongoose একটা ক্লাস। আমরা new mongoose.Schema() দিয়ে স্কিমা তৈরি করবো। এই স্কিমার মধ্যে কি কি থাকবে সেটা বলে দেয়ার জন্য একটা অবজেক্ট পাস করতে হবে। সেখানে থাকবে একজন মানুষের ফার্স্টনেইম, লাস্টনেইম, ইমেইল, বয়স, একটা বায়ো আর বৈবাহিক অবস্থা। + +```js +const mongoose = require('mongoose'); + +const personSchema = new mongoose.Schema({ + firstName: String, + lastName: String, + email: String, + age: Number, + bio: String, + single: Boolean, +}); + +mongoose + .connect('mongodb://loclhost:27017/mongo-demo') + .then(async () => { + console.log('Database connected'); + }) + .catch((e) => { + console.log(e); + }) + .finally(() => { + mongoose.connection.close(); + }); +``` + +আমরা আমাদের স্কিমা তৈরি করে ফেললাম। এবার আমরা মডেল তৈরি করবো। + +```js +const mongoose = require('mongoose'); + +const personSchema = new mongoose.Schema({ + firstName: String, + lastName: String, + email: String, + age: Number, + bio: String, + single: Boolean, +}); + +const Person = mongoose.model('Person', personSchema); + +mongoose + .connect('mongodb://loclhost:27017/mongo-demo') + .then(async () => { + console.log('Database connected'); + }) + .catch((e) => { + console.log(e); + }) + .finally(() => { + mongoose.connection.close(); + }); +``` + +এরপর আমরা আমাদের ডাটাবেজে ডাটা ইনসার্ট করবো। এই কাজটা অবশ্যই করতে হবে then ব্লকের ভেতর। আমরা প্রথমে কোনো ডাটা ইনপুট না দিয়ে empty object দিয়ে দেখি আউটপুট কেমন আসে। + +```js +const mongoose = require('mongoose'); + +const personSchema = new mongoose.Schema({ + firstName: String, + lastName: String, + email: String, + age: Number, + bio: String, + single: Boolean, +}); + +const Person = mongoose.model('Person', personSchema); + +mongoose + .connect('mongodb://loclhost:27017/mongo-demo') + .then(async () => { + console.log('Database connected'); + const person = new Person({}); + await person.save(); + console.log('Person created'); + }) + .catch((e) => { + console.log(e); + }) + .finally(() => { + mongoose.connection.close(); + }); +``` + +এবার আমরা ডাটাবেইজে কি সেভ হলো একটু দেখি। আপনারা কম্পাসে গিয়ে দেখতে পারবেন। আমি এখানে মঙ্গোডিবির ভিএস কোড এক্সটেনশন ব্যবহার করছি। সেখানে থেকেই আউটপুটটা দেখাচ্ছি। + +![Database](./images/database-1.png) + +দেখা যাচ্ছে আইডি ছাড়া আর কিছুই এখানে নেই। এবার আমরা ফার্স্টনেইম আর লাস্টনেইম দিয়ে দেখি। + +```js +const mongoose = require('mongoose'); + +const personSchema = new mongoose.Schema({ + firstName: String, + lastName: String, + email: String, + age: Number, + bio: String, + single: Boolean, +}); + +const Person = mongoose.model('Person', personSchema); + +mongoose + .connect('mongodb://loclhost:27017/mongo-demo') + .then(async () => { + console.log('Database connected'); + const person = new Person({}); + await person.save(); + console.log('Person created'); + }) + .catch((e) => { + console.log(e); + }) + .finally(() => { + mongoose.connection.close(); + }); +``` + +![db2](./images/db-2.png) + +এবার দেখা যাচ্ছে ফার্স্টনেইম আর লাস্টনেইম ছাড়া আর কিছু আসে নি। আমরা এখানে যেকোনো ডাটা ইনপুট করতে পারি। কারণ আমরা এখানে কোনো ভ্যালিডেশন করিনি। ভ্যালিডেশন না করার কারণে কেউ খালি ডাটা দিতে পারে, কেউ একটা দিবে বাকিটা দিবে না এরকম করতে পারে। আমি তা চাই না। আমি এখন কি করবো? আমাদের স্কিমাতে ডাটা ভ্যালিডেশনের কিছু অপশন রাখবো। + +```js +const mongoose = require('mongoose'); + +const personSchema = new mongoose.Schema({ + firstName: { + type: String, + required: true, + minlength: [3, 'Minimum 3 chars'], + maxlength: [20, 'Maximum 20 chars'], + }, + lastName: { + type: String, + required: true, + minlength: [3, 'Minimum 3 chars'], + maxlength: [20, 'Maximum 20 chars'], + }, + email: { + type: String, + required: true, + validate: { + validator: function (v) { + return v.endsWith('.com'); + }, + message: 'Invalid email formats', + }, + }, + age: Number, + bio: String, + single: Boolean, +}); + +const Person = mongoose.model('Person', personSchema); + +mongoose + .connect('mongodb://localhost:27017/mongo-demo') + .then(async () => { + console.log('Database connected'); + + await person.save(); + }) + .catch((e) => { + console.log(e); + }) + .finally(() => { + mongoose.connection.close(); + }); +``` + +ফার্স্টনেইমের ক্ষেত্রে আমাদের ডাটা টাইপ স্ট্রিং, এটা খালি রাখা যাবে না তার জন্য `required: true`, মিনিমাম ৩ অক্ষরের হতে হবে এবং ম্যাক্সিমাম ২০ অক্ষরের হতে হবে এরকম করে তৈরি করলাম। লাস্টনেইমের জন্যও তা সেইম। এরপর ইমেইল। টাইপ এবং রিকোয়ারড আগের মতোই স্ট্রিং এবং true। এরপর আমরা একটা ভ্যালিডেশন করবো। Mongoose এ উপরের মতো করে ভ্যালিডেশন করা হয় ইমেইল, একটা ফাংশন লিখে। বাকিগুলোতে আমাদের ভ্যালিডেশনের তেমন প্রয়োজন নেই। কেউ এই ৩টা দিলেও সমস্যা নাই, না দিলেও সমস্যা নাই। আমরা চাই ইউজার অন্তত তার ফার্স্টনেইম, লাস্টনেইম আর ইমেইল এই তিনটা ফিল্ড অবশ্যই দিবে। + +এবার আমরা একটু দেখি যদি ইমেইল ফিল্ড না দিয়ে বাকিগুলো দিই তাহলে তা নিবে নাকি এরর দিবে। + +```js +const mongoose = require('mongoose'); + +const personSchema = new mongoose.Schema({ + firstName: { + type: String, + required: true, + minlength: [3, 'Minimum 3 chars'], + maxlength: [20, 'Maximum 20 chars'], + }, + lastName: { + type: String, + required: true, + minlength: [3, 'Minimum 3 chars'], + maxlength: [20, 'Maximum 20 chars'], + }, + email: { + type: String, + required: true, + validate: { + validator: function (v) { + return v.endsWith('.com'); + }, + message: 'Invalid email formats', + }, + }, + age: Number, + bio: String, + single: Boolean, +}); + +const Person = mongoose.model('Person', personSchema); + +mongoose + .connect('mongodb://localhost:27017/mongo-demo') + .then(async () => { + console.log('Database connected'); + const person = new Person({ + firstName: 'Aditya', + lastName: 'Chakraborty', + age: 30, + bio: 'Backend Developer', + single: true, + }); + await person.save(); + console.log('Person created'); + console.log(person); + }) + .catch((e) => { + console.log(e); + }) + .finally(() => { + mongoose.connection.close(); + }); +``` + +দেখা যাবে এটা ডাটাবেজে সেইভ হবে না আর বড়সড় একটা এরর দিবে নিচের মতো। + +![error](./images/error.png) + +এবার সব যদি ঠিকমতো দিই তাহলে কি হবে একটু দেখি। + +```js +const mongoose = require('mongoose'); + +const personSchema = new mongoose.Schema({ + firstName: { + type: String, + required: true, + minlength: [3, 'Minimum 3 chars'], + maxlength: [20, 'Maximum 20 chars'], + }, + lastName: { + type: String, + required: true, + minlength: [3, 'Minimum 3 chars'], + maxlength: [20, 'Maximum 20 chars'], + }, + email: { + type: String, + required: true, + validate: { + validator: function (v) { + return v.endsWith('.com'); + }, + message: 'Invalid email formats', + }, + }, + age: Number, + bio: String, + single: Boolean, +}); + +const Person = mongoose.model('Person', personSchema); + +mongoose + .connect('mongodb://localhost:27017/mongo-demo') + .then(async () => { + console.log('Database connected'); + const person = new Person({ + firstName: 'Aditya', + lastName: 'Chakraborty', + email: 'aditya@example.com', + age: 30, + bio: 'Backend Developer', + single: true, + }); + await person.save(); + console.log('Person created'); + console.log(person); + }) + .catch((e) => { + console.log(e); + }) + .finally(() => { + mongoose.connection.close(); + }); +``` + +এবার দেখা যাবে সেটা সাক্সেসফুল হয়েছে। আর ডাটাবেজেও তা ক্রিয়েট হয়েছে। + +![success](./images/success.png) +![db-3](./images/db-3.png) + +আজ মোটামুটি Mongoose দিয়ে কিভাবে ডাটাবেজ কানেক্ট করা যায়, কিভাবে স্কিমা তৈরি করতে হয়, কিভাবে মডেল তৈরি করতে হয় তার একটা মোটামুটি ধারণা আমরা পেলাম। ব্যাকএন্ডের পূর্বের ক্লাসের এটাই শেষ ক্লাস। পরবর্তীতে আমরা আবার আমাদের অ্যাটেন্ডেন্স মডেলের প্রজেক্টে ফিরে যাবে। আপনাদের জন্য একটা টাস্ক হলো, [MongooseJs Docs](https://mongoosejs.com/docs/) থেকে আপনারা Schemas, SchemaTypes, Connections, Models, Documents, Subdocuments, Queries and Validation স্টাডি করবেন এবং প্র্যাকটিস করবেন। + +## Resource for this lecture + +এই লেকচারের সমস্ত রিসোর্স [লেকচার ২০](../../resources/lecture-20/README.md) এ পাবেন। + +## Source Code + +এই লেকচারের সোর্স কোডসমূহ এই [লিংক](../../src/mongo-demo/) এ পাবেন। + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/full-stack-army/class-overview/Lecture-20/images/database-1.png b/full-stack-army/class-overview/Lecture-20/images/database-1.png new file mode 100644 index 0000000..7f44ebb Binary files /dev/null and b/full-stack-army/class-overview/Lecture-20/images/database-1.png differ diff --git a/full-stack-army/class-overview/Lecture-20/images/db-2.png b/full-stack-army/class-overview/Lecture-20/images/db-2.png new file mode 100644 index 0000000..f03653b Binary files /dev/null and b/full-stack-army/class-overview/Lecture-20/images/db-2.png differ diff --git a/full-stack-army/class-overview/Lecture-20/images/db-3.png b/full-stack-army/class-overview/Lecture-20/images/db-3.png new file mode 100644 index 0000000..4471e58 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-20/images/db-3.png differ diff --git a/full-stack-army/class-overview/Lecture-20/images/entity-diagram.png b/full-stack-army/class-overview/Lecture-20/images/entity-diagram.png new file mode 100644 index 0000000..2e12ffd Binary files /dev/null and b/full-stack-army/class-overview/Lecture-20/images/entity-diagram.png differ diff --git a/full-stack-army/class-overview/Lecture-20/images/error.png b/full-stack-army/class-overview/Lecture-20/images/error.png new file mode 100644 index 0000000..b158543 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-20/images/error.png differ diff --git a/full-stack-army/class-overview/Lecture-20/images/example.png b/full-stack-army/class-overview/Lecture-20/images/example.png new file mode 100644 index 0000000..24a3384 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-20/images/example.png differ diff --git a/full-stack-army/class-overview/Lecture-20/images/mongodb-1.png b/full-stack-army/class-overview/Lecture-20/images/mongodb-1.png new file mode 100644 index 0000000..bbc32b3 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-20/images/mongodb-1.png differ diff --git a/full-stack-army/class-overview/Lecture-20/images/schematypes.png b/full-stack-army/class-overview/Lecture-20/images/schematypes.png new file mode 100644 index 0000000..8552698 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-20/images/schematypes.png differ diff --git a/full-stack-army/class-overview/Lecture-20/images/success.png b/full-stack-army/class-overview/Lecture-20/images/success.png new file mode 100644 index 0000000..b18ef0f Binary files /dev/null and b/full-stack-army/class-overview/Lecture-20/images/success.png differ diff --git a/full-stack-army/class-overview/Lecture-21/README.md b/full-stack-army/class-overview/Lecture-21/README.md new file mode 100644 index 0000000..192ae16 --- /dev/null +++ b/full-stack-army/class-overview/Lecture-21/README.md @@ -0,0 +1,22 @@ +# Lecture 21 - QNA on Express 101 and Books + +## মোটামুটি লেভেলের প্রব্লেম সলভার হতে গেলে কি কি ডাটা স্ট্রাকচার এবং অ্যালগরিদম লাগতে পারে? + +মোটামুটি লেভেলের প্রব্লেম সলভার হতে গেলে নিচের ডাটা স্ট্রাকচার এবং অ্যালগরিদম জানা থাকতে হবে। + +- **ডাটা স্ট্রাকচার** + + - Stack + - Queue + - Linked List + - Tree + - Hashmap + - Hash Table + - Heap + +- **অ্যালগরিদম** + + - Searching Sorting + - Dynamic Programming + - Recursive Programming + - Divide and Conquer diff --git a/full-stack-army/class-overview/Lecture-22/README.MD b/full-stack-army/class-overview/Lecture-22/README.MD new file mode 100644 index 0000000..7822163 --- /dev/null +++ b/full-stack-army/class-overview/Lecture-22/README.MD @@ -0,0 +1,669 @@ +# Lecture 22 - Authentication System from Pseudo Code to Real Code + +[লেকচার ১৩](../Lecture-13/README.md) তে আমরা অ্যাটেনডেন্স সিস্টেম প্রজেক্টের মডেল তৈরি করেছিলাম। আর অথেনটিকেশনের রেজিস্ট্রেশন আর লগইন প্রসেসের সুডোকোড লিখেছিলাম। সেগুলোসহ reset password এর সুডোকোড নিচে দেয়া হলো। এরপর আমরা এই সুডোকোডকে কোডে রূপান্তরিত করবো। + +**Registration Process:** + +```txt +Start +name = input() +email = input() +password = input() +if name && email && password is invalid: + return 400 error + +user = find user with email +if user found: + return 400 error + +hash = hash password +user = save name, email, hash to user model +return 201 +End +``` + +**Login Process:** + +```txt +Start +email = input() +password = input() + +user = find user with email +if user not found: + return 400 error + +if password not equal to user hash: + return 400 error + +token = generate token using user +return token +End +``` + +**Reset Password:** + +```txt +Start +new-password = Input() +old-password = Input() +TODO + I +if old-password not equal to user.hash password: + return 400 error +else hashNewPassword = hash new-password + save hashNewPassword + return 201 +End +``` + +অনেকের মনে প্রশ্ন থাকতে পারে, আমরা মডেল কেন তৈরি করি। এখন এই প্রশ্নের উত্তর পেতে হলে ডাটাবেজ একটু হলেও আমাদের বুঝতে হবে। এখন মডেলের সাথে ডাটাবেজের কি সম্পর্ক? আমরা MVC টার্মটা প্রায়ই শুনে থাকি। M মানে হলো মডেল। মডেল মানে হলো আমাদের ডাটার একটা শেইপ। কিভাবে আমরা ডাটা ডাটাবেজে স্টোর করবো সেটাই হচ্ছে মডেল। আমরা চাইলে সরাসরি ডাটাবেজ নিয়ে কাজ করতে পারি। যেমন মঙ্গোডিবিতে json রিটার্ন করে। সেই json আমরা জাভাস্ক্রিপ্টে অবজেক্টে পরিণত করে কাজ করতে পারি। তার মানে আমরা কোনোরকম কোনো মডেল মাঝখানে না রেখে সুন্দরভাবে মঙ্গোডিবি নিয়ে কাজ করতে পারি। কিন্তু অন্যান্য ডাটাবেজের ক্ষেত্রে আমরা কি করবো? যেমন SQL ডাটাবেজ। আমরা যখন কুয়েরি ব্যবহার করে ডাটাগুলোকে বের করে আনি সেগুলো আসে টেবিল আকারে। এখন এই টেবিল নিয়ে তো আমরা কাজ করতে পারবো না। আমাদের সেই টেবিলগুলো অবজেক্টের পরিণত করতে হবে বা কোনো অ্যারেতে পরিণত করতে হবে বা কোনো ক্লাস নিয়ে সেই ক্লাসের শেইপে ফেলতে হবে। এই কাজগুলো খুব রিপিটেটিভ কাজ। তাই এই কাজগুলোকে আমরা একটা লাইব্রেরির মধ্যে রাখতে পারি। পরবর্তীতে যেকোনো প্রজেক্টে আমরা সেগুলো প্রয়োজন বুঝে ব্যবহার করতে পারি। সেরকমই একটা লাইব্রেরি হলো [Mongoose](https://mongoosejs.com/)। এটা নিয়ে বিস্তারিত [লেকচার ২০](../Lecture-20/README.md) এ আলোচনা করা হয়েছিল। এর কাজ হচ্ছে ডাটাবেজের সাথে ডিল করা। আমরা ডাটাবেইজের একটা শেইপ বা মডেল তৈরি করবো, সেটা আমরা আমাদের লাইব্রেরিকে দিয়ে দিবো। এবার লাইব্রেরির কাজ হলো ডাটাবেজের সাথে আমাদের মডেল নিয়ে ডিল করা। এ কারণেই আমরা মডেল তৈরি করি। + +এবার আমাদের সিস্টেমে ডাটাবেজ নাই। আগে ডাটাবেজ কানেক্ট করতে হবে। নাহয় আমরা ডাটা স্টোর করতে পারবো না। আর ডাটাবেজ কিভাবে কানেক্ট করতে হয় তা বিস্তারিত [লেকচার ২০](../Lecture-20/README.md) এ দেখানো হয়েছিল। যাদের ইনস্টল করতে প্রব্লেম হয়েছিল তাদের জন্য ভিডিওতে ইনস্টলেশন সিস্টেম দেখানো হয়েছে। আপনারা দেখে নিবেন। + +আমরা আমাদের ফোল্ডারে db.js নামে একটা ফাইল ক্রিয়েট করবো। সেখানে গিয়ে আমরা আমাদের mongoose এর মাধ্যমে ডাটাবেজ কানেক্ট করবো। কিভাবে কানেক্ট করতে হবে তা অলরেডি আপনারা জানেন। আগের লেকচারে দেখানো হয়েছিল। + +```js +// db.js + +const mongoose = require('mongoose'); + +function connectDB(connectionStr) { + return mongoose.connect(connectionStr); +} + +module.exports = connectDB; +``` + +আমরা এখানে সরাসরি Mongoose কানেক্ট করবো না। আমরা এখানে একটা ফাংশন তৈরি করবো। যেটাকে আমরা পুরো অ্যাপ্লিকেশনের যেখানে খুশি সেখানে ব্যবহার করে কানেক্ট করতে পারবো। + +এবার আমরা server.js এ গিয়ে এই ফাংশনটাকে ইমপোর্ট করবো। + +```js +// server.js + +const express = require('express'); +const connectDB = require('./db'); + +const app = express(); + +app.get('/', (req, res) => { + const obj = { + name: 'Ayman', + email: 'ayman@example.com', + }; + res.json(obj); +}); + +app.listen(4000, () => { + console.log("I'm listening on port 4000"); +}); +``` + +এবার আমরা ডাটাবেজ কানেক্ট করবো। + +```js +// server.js + +const express = require('express'); +const connectDB = require('./db'); + +const app = express(); + +app.get('/', (req, res) => { + const obj = { + name: 'Ayman', + email: 'ayman@example.com', + }; + res.json(obj); +}); + +app.listen(4000, () => { + console.log("I'm listening on port 4000"); +}); + +connectDB('mongodb://localhost:27017/attendance-db') + .then(() => { + console.log('Database Connected'); + app.listen(4000, () => { + console.log("I'm listening on port 4000"); + }); + }) + .catch((e) => console.log(e)); +``` + +এবার আমরা লেকচার ১২ তে রেজিস্ট্রেশনের যে ড্রয়িং করেছিলাম সেগুলো একটু দেখি। + +![registration](./images/registration-process.jpg) + +আমাদের প্রথম কাজ হলো /register রাউট হ্যান্ডেল করা। আমরা আলাদাভাবে রাউটস ফোল্ডারে হ্যান্ডেল করতে পারি। তবে এখন আমরা আপাতত server.js এই হ্যান্ডেল করছি। আমরা যে সুডোকোড লিখেছিলাম সেই অনুযায়ী কোডে পরিণত করবো। + +```js +// server.js + +const express = require('express'); +const connectDB = require('./db'); + +const app = express(); + +app.post('/register', async (req, res, next) => {}); + +app.get('/', (req, res) => { + const obj = { + name: 'Ayman', + email: 'ayman@example.com', + }; + res.json(obj); +}); + +app.listen(4000, () => { + console.log("I'm listening on port 4000"); +}); + +connectDB('mongodb://localhost:27017/attendance-db') + .then(() => { + console.log('Database Connected'); + app.listen(4000, () => { + console.log("I'm listening on port 4000"); + }); + }) + .catch((e) => console.log(e)); +``` + +আমাদের সুডোকোডে প্রথমে দেখা যাচ্ছে কোনো জায়গা থেকে আমাদের name, email আর password আসবে। একটা রিকোয়েস্টে ইনপুট আসতে পারে মূলত পাঁচটা জায়গা থেকে। সেগুলো হলো - + +- req Body +- req Param +- req Query +- req Header +- req Cookies + +যে ডাটাগুলো ইউজার ফর্ম থেকে পাঠায় সাদারণত সেই রিকোয়েস্টগুলো আসে বডি থেকে। আমরা প্রথমে আমাদের বডিতে কি আছে একটু দেখে নিই। আপনারা পোস্টম্যান দিয়ে সেই রিকোয়েস্ট পাঠাতে পারেন। আমি ভিএস কোড এক্সটেনশন thunder client ব্যবহার করছি। + +```js +// server.js + +const express = require('express'); +const connectDB = require('./db'); + +const app = express(); + +app.use(express.json()); + +app.post('/register', async (req, res, next) => { + console.log(req.body); +}); + +app.get('/', (req, res) => { + const obj = { + name: 'Ayman', + email: 'ayman@example.com', + }; + res.json(obj); +}); + +app.listen(4000, () => { + console.log("I'm listening on port 4000"); +}); + +connectDB('mongodb://localhost:27017/attendance-db') + .then(() => { + console.log('Database Connected'); + app.listen(4000, () => { + console.log("I'm listening on port 4000"); + }); + }) + .catch((e) => console.log(e)); +``` + +![register1](./images/register1.png) + +এখন এখানে বডিতে যা লিখলাম সেগুলো আমাদের রাউটার হ্যান্ডেল করতে পারবে ঠিক কিন্তু কোনো রেসপন্স ব্যাক করবে না। কারণ এখানে রেসপন্স ব্যাক করার মতো কোনো কোডই আমরা লিখিনি। তো সে কি করবে সে হ্যাং হয়ে বসে থাকবে আর বডিতে যে ডাটা লিখেছিলাম সেটা কনসোলে লগ করবে। আপাতত আমরা সেটাই দেখি। + +![reg2](./images/reg-2.png) +![console](./images/console.png) + +দেখুন যখন আমরা রিকোয়েস্ট পাঠিয়েছি সেটা গোল গোল ঘুরছে আর কনসোলে সেই বডিটা লগ হয়েছে। বডিটা লগ হওয়ার জন্য আমরা express.json() নামে একটা মিডলওয়্যার লিখেছি আমরা। তো এখন আমরা name, email আর password বডি থেকে নিয়ে নিই। + +```js +// server.js + +const express = require('express'); +const connectDB = require('./db'); + +const app = express(); + +app.use(express.json()); + +app.post('/register', async (req, res, next) => { + const { name, email, password } = req.body; +}); + +app.get('/', (req, res) => { + const obj = { + name: 'Ayman', + email: 'ayman@example.com', + }; + res.json(obj); +}); + +app.listen(4000, () => { + console.log("I'm listening on port 4000"); +}); + +connectDB('mongodb://localhost:27017/attendance-db') + .then(() => { + console.log('Database Connected'); + app.listen(4000, () => { + console.log("I'm listening on port 4000"); + }); + }) + .catch((e) => console.log(e)); +``` + +অবজেক্ট ডিস্ট্রাকচারিং ব্যবহার করলাম। এবার আমরা ডাটা ভ্যালিডেশন করবো। সুডোকোডে আছে যদি যেকোনো একটা ইনপুট না দেয়া হয় তাহলে 400 রিটার্ন করবে। + +```js +// server.js + +const express = require('express'); +const connectDB = require('./db'); + +const app = express(); + +app.use(express.json()); + +app.post('/register', async (req, res, next) => { + const { name, email, password } = req.body; + + if (!name || !email || !password) { + return res.status(400).json({ message: 'Invalid Data' }); + } +}); + +app.get('/', (req, res) => { + const obj = { + name: 'Ayman', + email: 'ayman@example.com', + }; + res.json(obj); +}); + +app.listen(4000, () => { + console.log("I'm listening on port 4000"); +}); + +connectDB('mongodb://localhost:27017/attendance-db') + .then(() => { + console.log('Database Connected'); + app.listen(4000, () => { + console.log("I'm listening on port 4000"); + }); + }) + .catch((e) => console.log(e)); +``` + +যদি তিনটা ইনপুটের যেকোনো একটা দেয়া না হয় তবে সে রিটার্ন করবে `{ message: 'Invalid Data' }` এই ম্যাসেজটা। + +![reg-3](./images/reg-3.png) + +দেখুন পাসওয়ার্ড না দেয়াতে এটি `{ message: 'Invalid Data' }` শো করছে। এখন ডাটা ইনভ্যালিড হলে কি হবে সেটা লিখলাম। এটা একটা প্যাটার্ন। একে বলে এরর ফার্স্ট প্যাটার্ন। প্রথমে যা যা এরর হতে পারে সেগুলো হ্যান্ডেল করাকে বলে এরর ফার্স্ট প্যাটার্ন। এবার ভ্যালিড হলে কি হবে তা লেখা যাক। সুডোকোডে লেখা আছে ইমেইলের সাহায্যে ইউজারকে বের করে আনা। মানে ইউজার যে ইমেইল দিয়েছে তা চেক করা। যদি ইমেইল থেকে থাকে তাহলে তো খুবই সিম্পল, আমরা একাউন্ট ক্রিয়েট করতে দিবো না। আর যদি না থাকে তাহলে ধরে নিবো সে নতুন ইউজার, তাকে আমরা একাউন্ট ক্রিয়েট করতে দিবো। এখন ইউজারকে যে বের করবো, ইউজার আছে কোথায়? ডাটাবেজে। আর ডাটাবেজের সাথে কমিউনিকেট করার দায়িত্ব মডেলের। মডেল ক্রিয়েট করার পরই Mongoose এর কাজ শেষ। মডেল ক্রিয়েট করা মানেই Mongoose তার সমস্ত পাওয়ার মডেলকে দিয়ে দিয়েছে। এরপর থেকে আমরা মডেল নিয়ে কাজ করতে পারি। আমরা আমাদের User মডেলকে আমাদের সার্ভারে নিয়ে আসবো। কোনো কিছু find করার জন্য মঙ্গোডিবিতে একটা মেথড আছে `findOne`। সেটা দিয়ে আমরা ইউজার মডেল থেকে ইমেইল বের করে আনবো। + +```js +// server.js + +const express = require('express'); +const connectDB = require('./db'); +const User = require('./models/User'); + +const app = express(); + +app.use(express.json()); + +app.post('/register', async (req, res, next) => { + const { name, email, password } = req.body; + + if (!name || !email || !password) { + return res.status(400).json({ message: 'Invalid Data' }); + } + + let user = await User.findOne({ email }); + if (user) { + return res.status(400).json({ message: 'User already exists' }); + } + + user = new User({ name, email, password }); + await user.save(); + + return res.status(201).json({ message: 'User Created Successfully', user }); +}); + +app.get('/', (req, res) => { + const obj = { + name: 'Ayman', + email: 'ayman@example.com', + }; + res.json(obj); +}); + +app.listen(4000, () => { + console.log("I'm listening on port 4000"); +}); + +connectDB('mongodb://localhost:27017/attendance-db') + .then(() => { + console.log('Database Connected'); + app.listen(4000, () => { + console.log("I'm listening on port 4000"); + }); + }) + .catch((e) => console.log(e)); +``` + +উপরে দেখলাম যদি ইমেইল থেকে থাকে তাহলে 400 স্ট্যাটাস রিটার্ন করবে। আর যদি না থাকে তাহলে আপাতত নেইম, ইমেইল আর পাসওয়ার্ড দিয়ে একটা নতুন ইউজার ক্রিয়েট করবে। এবং সেটা ডাটাবেজে সেইভ করতে হবে। ইউজার সেইভ হয়ে গেলে আমরা যে ইউজারটা ক্রিয়েট হলো সেটাকে রিটার্ন করে দিলাম। এবার আমরা একটু চেক করবো। + +![reg-4](./images/reg-4.png) + +দেখুন এটা আমাদের সাক্সেস ম্যাসেজ দিয়েছে। আমরা যদি ডাটাবেজে গিয়ে দেখি দেখবো সেখানেও সেইভ হয়ে গিয়েছে। + +![db-1](./images/db-1.png) + +এখন এখানে একটা সমস্যা হলো আমরা পাসওয়ার্ডটা খুব সহজেই দেখতে পাচ্ছি। কেউ যদি আমাদের ডাটাবেজের এক্সেস পায় তাহলে সে সহজেই আমাদের একাউন্ট হ্যাক করে ফেলতে পারে। সেটার সল্যুশন হিসেবে আমাদের পাসওয়ার্ডটাকে হ্যাশ করতে হবে। এর জন্য `bcrypt` নামে একটা লাইব্রেরি আছে। আমরা প্রথমে সেটা ইনস্টল করে নিবো। এরপর আমাদের সার্ভারে সেটা ইমপোর্ট করে নিবো। + +```js +// server.js + +const express = require('express'); +const connectDB = require('./db'); +const User = require('./models/User'); +const bcrypt = require('bcryptjs'); + +const app = express(); + +app.use(express.json()); + +app.post('/register', async (req, res, next) => { + const { name, email, password } = req.body; + + if (!name || !email || !password) { + return res.status(400).json({ message: 'Invalid Data' }); + } + + let user = await User.findOne({ email }); + if (user) { + return res.status(400).json({ message: 'User already exists' }); + } + + user = new User({ name, email, password }); + + const salt = await bcrypt.genSalt(10); + const hash = await bcrypt.hash(password, salt); + user.password = hash; + + await user.save(); + + return res.status(201).json({ message: 'User Created Successfully', user }); +}); + +app.get('/', (req, res) => { + const obj = { + name: 'Ayman', + email: 'ayman@example.com', + }; + res.json(obj); +}); + +app.listen(4000, () => { + console.log("I'm listening on port 4000"); +}); + +connectDB('mongodb://localhost:27017/attendance-db') + .then(() => { + console.log('Database Connected'); + app.listen(4000, () => { + console.log("I'm listening on port 4000"); + }); + }) + .catch((e) => console.log(e)); +``` + +প্রথমে আমরা কত রাউন্ডের পাসওয়ার্ড চাইছি সেটা লিখে দিতে হবে। আমরা ১০ দিলাম। এরপর হ্যাশ করলাম। সেই হ্যাশ পাসওয়ার্ডকে আমরা পাসওয়ার্ড হিসেবে সেইভ করলাম। চলুন এবার চেক করে দেখি। + +একটা জিনিস খেয়াল করুন আমরা এক্সিসটিং ইমেইল দিয়ে যখন রিকোয়েস্ট দিয়েছি তা আমাদের এরর রিটার্ন করেছে। + +![reg-5](./images/reg-5.png) + +এবার ভিন্ন নাম আর ইমেইল দিয়ে দেখি। + +![reg-6](./images/reg-6.png) +![reg-6](./images/db-2.png) + +দেখুন পাসওয়ার্ড হ্যাশ হয়ে গেছে। এই পাসওয়ার্ড পেলেও এটা দিয়ে লগইন করা যাবে না। আর এটা থেকে আমাদের পাসওয়ার্ড পাওয়া সম্ভবও না। + +আমরা ইউজার মডেলে গতো ক্লাসের মতো ভ্যালিডেশন ব্যবহার করবো। + +```js +// User.js + +const { model, Schema } = require('mongoose'); + +const userSchema = new Schema({ + name: { + type: String, + required: true, + minlength: 3, + maxlength: 10, + }, + email: { + type: String, + required: true, + validate: { + validator: function (v) { + return /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(v); + }, + message: (prop) => `Invalid Email: ${prop.value}`, + }, + }, + password: { + type: String, + minlength: [6, 'Password is too short'], + required: true, + }, + roles: { + type: [String], + required: true, + default: ['STUDENT'], + }, + accountStatus: { + type: String, + enum: ['PENDING', 'ACTIVE', 'REJECTED'], + default: 'PENDING', + required: true, + }, +}); + +const User = model('User', userSchema); + +module.exports = User; +``` + +এগুলো আর বিশ্লেষণ করার দরকার আছে বলে মনে হয় না। শুধু enum সম্পর্কে একটু বলি। enum একটা অ্যারে যা ঐ অ্যারের মধ্যে থাকা ভ্যালু ছাড়া আর কোনো ভ্যালু এক্সেপ্ট করবে না। + +আপনারা আপনাদের মতো করে এই ভ্যালিডেশন চেক করে দেখতে পারেন postman বা thunder client এ। + +এবার আমরা আমাদের সার্ভারে একটা গ্লোবাল এরর হ্যান্ডলার বানাবো যেন সার্ভার রিলেটেড কোনো এরর হলে তা আমাদের শো করে। এবং আমাদের রাউটকে একটা try catch ব্লকের ভিতর এনে catch ব্লকে সেই গ্লোবাল এররের মধ্যে রিকোয়েস্ট পাঠিয়ে দেবো + +```js +// server.js + +const express = require('express'); +const connectDB = require('./db'); +const User = require('./models/User'); +const bcrypt = require('bcryptjs'); + +const app = express(); + +app.use(express.json()); + +app.post('/register', async (req, res, next) => { + const { name, email, password } = req.body; + + if (!name || !email || !password) { + return res.status(400).json({ message: 'Invalid Data' }); + } + + try { + let user = await User.findOne({ email }); + if (user) { + return res.status(400).json({ message: 'User already exists' }); + } + user = new User({ name, email, password }); + const salt = await bcrypt.genSalt(10); + const hash = await bcrypt.hash(password, salt); + user.password = hash; + await user.save(); + return res.status(201).json({ message: 'User Created Successfully', user }); + } catch (error) { + next(error); + } +}); + +app.get('/', (req, res) => { + const obj = { + name: 'Ayman', + email: 'ayman@example.com', + }; + res.json(obj); +}); + +app.listen(4000, () => { + console.log("I'm listening on port 4000"); +}); + +app.use((err, req, res, next) => { + console.log(err); + res.status(500).json({ message: 'Server Error Occurred' }); +}); + +connectDB('mongodb://localhost:27017/attendance-db') + .then(() => { + console.log('Database Connected'); + app.listen(4000, () => { + console.log("I'm listening on port 4000"); + }); + }) + .catch((e) => console.log(e)); +``` + +এবার আমরা লগইন এর রাউট নিয়ে কাজ করবো। + +```js +// server.js + +const express = require('express'); +const connectDB = require('./db'); +const User = require('./models/User'); +const bcrypt = require('bcryptjs'); + +const app = express(); + +app.use(express.json()); + +app.post('/register', async (req, res, next) => { + const { name, email, password } = req.body; + + if (!name || !email || !password) { + return res.status(400).json({ message: 'Invalid Data' }); + } + + try { + let user = await User.findOne({ email }); + if (user) { + return res.status(400).json({ message: 'User already exists' }); + } + user = new User({ name, email, password }); + const salt = await bcrypt.genSalt(10); + const hash = await bcrypt.hash(password, salt); + user.password = hash; + await user.save(); + return res.status(201).json({ message: 'User Created Successfully', user }); + } catch (error) { + next(error); + } +}); + +app.post('/login', async (req, res, next) => { + const { email, password } = req.body; + try { + const user = await User.findOne({ email }); + + if (!user) { + return res.status(400).json({ message: 'Invalid Credential' }); + } + + const isMatch = await bcrypt.compare(password, user.password); + if (!isMatch) { + return res.status(400).json({ message: 'Invalid Credential' }); + } + + delete user._doc.password; + return res.status(200).json({ message: 'Login Successful', user }); + } catch (e) { + next(e); + } +}); + +app.get('/', (req, res) => { + const obj = { + name: 'Ayman', + email: 'ayman@example.com', + }; + res.json(obj); +}); + +app.listen(4000, () => { + console.log("I'm listening on port 4000"); +}); + +app.use((err, req, res, next) => { + console.log(err); + res.status(500).json({ message: 'Server Error Occurred' }); +}); + +connectDB('mongodb://localhost:27017/attendance-db') + .then(() => { + console.log('Database Connected'); + app.listen(4000, () => { + console.log("I'm listening on port 4000"); + }); + }) + .catch((e) => console.log(e)); +``` + +সুডোকোড অনুযায়ী আমরা বডি থেকে ইমেইল পাসওয়ার্ড নিলাম। এরপর ইমেইল ম্যাচ করে কিনা দেখবো। যদি ম্যাচ না হয় তাহলে এরর দেবে। আর যদি ম্যাচ হয় এরপর পাসওয়ার্ড ম্যাচ করে কিনা দেখবে। পাসওয়ার্ড কমপেয়ার করার জন্য আমরা bcrypt.compare() মেথড ব্যবহার করবো। যদি পাসওয়ার্ড ম্যাচ না করে এরর দিবে। আর ম্যাচ করলে সাক্সেস ম্যাসেজ দিবে সাথে ইউজার অবজেক্টটা দেখাবে। এখন আমি চাইনা ক্লায়েন্টকে হ্যাশ করা পাসওয়ার্ডটা দেখাতে। তাই রেসপন্স ব্যাক করার আগে আমরা আমাদের পাসওয়ার্ডকে ডিলিট করে দিবো। অবজেক্ট থেকে ডিলিট করা একদম সোজা। জাস্ট এক লাইনের কোড। `delete user._doc.password;`। `_doc` দেয়ার কারণ হচ্ছে আমরা ডাটাবেজে যতো ডাটা সেইভ করি সব থাকে `_doc` প্রোপার্টির মধ্যে। + +এবার একটু আমরা আমাদের কোড কাজ করছে কিনা ট্রাই করি। + +![login](./images/login-1.png) + +কাজ করছে। লক্ষ্য করুন অবজেক্টের মধ্যে কিন্তু পাসওয়ার্ড দেখা যাচ্ছে না। কারণ রেসপন্স ব্যাক করার পূর্বেই আমরা তা ডিলিট করে দিয়েছি। + +যদি পাসওয়ার্ড ভুল দিই তাহলে এরর দিচ্ছে দেখুন। তার মানে আমাদের কোড কাজ করছে। + +![login](./images/login-2.png) + +লগইন সুডোকোডের মধ্যে টোকেন জেনারেট করার কথা আছে। সেটা আমরা পরের ক্লাসে করবো। + +রিসেট পাসওয়ার্ডের কাজও আমরা নেক্সট ক্লাসে করবো। + +## Resource for this lecture + +এই লেকচারের সমস্ত রিসোর্স [লেকচার ২২](../../resources/lecture-22/README.md) এ পাবেন। + +## Source Code + +এই লেকচারের সোর্স কোডসমূহ এই [লিংক](../../src/lecture-22/) এ পাবেন। + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/full-stack-army/class-overview/Lecture-22/images/console.png b/full-stack-army/class-overview/Lecture-22/images/console.png new file mode 100644 index 0000000..3326193 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-22/images/console.png differ diff --git a/full-stack-army/class-overview/Lecture-22/images/db-1.png b/full-stack-army/class-overview/Lecture-22/images/db-1.png new file mode 100644 index 0000000..95d1853 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-22/images/db-1.png differ diff --git a/full-stack-army/class-overview/Lecture-22/images/db-2.png b/full-stack-army/class-overview/Lecture-22/images/db-2.png new file mode 100644 index 0000000..e2e94cf Binary files /dev/null and b/full-stack-army/class-overview/Lecture-22/images/db-2.png differ diff --git a/full-stack-army/class-overview/Lecture-22/images/login-1.png b/full-stack-army/class-overview/Lecture-22/images/login-1.png new file mode 100644 index 0000000..fb5e359 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-22/images/login-1.png differ diff --git a/full-stack-army/class-overview/Lecture-22/images/login-2.png b/full-stack-army/class-overview/Lecture-22/images/login-2.png new file mode 100644 index 0000000..fe41a8f Binary files /dev/null and b/full-stack-army/class-overview/Lecture-22/images/login-2.png differ diff --git a/full-stack-army/class-overview/Lecture-22/images/login-process.jpg b/full-stack-army/class-overview/Lecture-22/images/login-process.jpg new file mode 100644 index 0000000..11a3c2d Binary files /dev/null and b/full-stack-army/class-overview/Lecture-22/images/login-process.jpg differ diff --git a/full-stack-army/class-overview/Lecture-22/images/reg-2.png b/full-stack-army/class-overview/Lecture-22/images/reg-2.png new file mode 100644 index 0000000..282592e Binary files /dev/null and b/full-stack-army/class-overview/Lecture-22/images/reg-2.png differ diff --git a/full-stack-army/class-overview/Lecture-22/images/reg-3.png b/full-stack-army/class-overview/Lecture-22/images/reg-3.png new file mode 100644 index 0000000..c68cec6 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-22/images/reg-3.png differ diff --git a/full-stack-army/class-overview/Lecture-22/images/reg-4.png b/full-stack-army/class-overview/Lecture-22/images/reg-4.png new file mode 100644 index 0000000..dc92fc1 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-22/images/reg-4.png differ diff --git a/full-stack-army/class-overview/Lecture-22/images/reg-5.png b/full-stack-army/class-overview/Lecture-22/images/reg-5.png new file mode 100644 index 0000000..957c0e6 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-22/images/reg-5.png differ diff --git a/full-stack-army/class-overview/Lecture-22/images/reg-6.png b/full-stack-army/class-overview/Lecture-22/images/reg-6.png new file mode 100644 index 0000000..cb26ce7 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-22/images/reg-6.png differ diff --git a/full-stack-army/class-overview/Lecture-22/images/register1.png b/full-stack-army/class-overview/Lecture-22/images/register1.png new file mode 100644 index 0000000..beb838a Binary files /dev/null and b/full-stack-army/class-overview/Lecture-22/images/register1.png differ diff --git a/full-stack-army/class-overview/Lecture-22/images/registration-process.jpg b/full-stack-army/class-overview/Lecture-22/images/registration-process.jpg new file mode 100644 index 0000000..96f7e53 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-22/images/registration-process.jpg differ diff --git a/full-stack-army/class-overview/Lecture-23/README.md b/full-stack-army/class-overview/Lecture-23/README.md new file mode 100644 index 0000000..298b607 --- /dev/null +++ b/full-stack-army/class-overview/Lecture-23/README.md @@ -0,0 +1,792 @@ +# Lecture 23 - Implement JWT and Refactor The Project Structure + +আমাদের অথেনটিকেশনের সব কাজ গত ক্লাসে আমরা করেছি। শুধু একটা কাজ বাকি ছিল JWT Token জেনারেট করা। ওটা আমরা আজ দেখবো। + +JWT নিয়ে কাজ করতে গেলে প্রথমে আমাদের জানতে হবে JWT কি? এটা কেন ব্যবহার করা হয়? সেশন কুকি দিয়েও তো কাজ করা যায়, তাহলে কেন JWT ব্যবহার করা হয়? + +এই API এর যুগে কি হচ্ছে? আমরা ফেসবুক থেকে ডাটা নিচ্ছি, আমরা গুগল থেকে ডাটা নিচ্ছি, আমরা AWS থেকে ডাটা নিচ্ছি। বিভিন্ন সার্ভার থেকে ডাটা নিয়ে আমরা একটা অ্যাপ্লিকেশনে মার্জ করছি। ধরেন আমরা ওয়েদার অ্যাপ্লিকেশন বানাবো, আমরা ওয়েদারের ডাটা অন্য জায়গা থেকে নিয়ে আসছি, আমরা ব্যাংকিং সিস্টেমের অ্যাপ্লিকেশন বানাবো, আমরা বিভিন্ন ব্যাংকের ডাটা সংগ্রহ করছি বিভিন্ন ব্যাংকের সার্ভার থেকে। এখন যদি আমরা তাদের সার্ভারে সেশন কুকিজ রাখতে চাই তারা তো আমাকে রাখতে দেবে না। আর তাদের সার্ভারে যদি থাকি তাহলে আমরা ভেরিফাই করতে পারবো না। তাই এমন একটা সিস্টেম দরকার যেটার মাধ্যমে আমরা খুব সহজে পোর্টেবল উপায়ে ভেরিফাই করতে পারি যে আমি ভেরিফায়েড ইউজার। এজন্য ব্যবহার করা হয় টোকেন। যেটাকে আমরা বলছি JSON Web Token (JWT)। এটা ইন্ডাস্ট্রি স্ট্যান্ডার্ড, সবাই ব্যবহার করে। এটা কোনো হ্যাশড বা এনক্রিপটেড সিস্টেম না। এটা হলো ওপেন, যাকে বলা হয় এনকোডেড। এনকোডেড মানে হলো দেখতে ভিন্ন, কিন্তু ডাটা ওপেন। এটাকে ডিকোড করে সঠিক ডাটা পাওয়া সম্ভব। কিন্তু হ্যাশড বা এনক্রিপটেড ডাটা থেকে কখনই আসল ডাটা বের করে আনা সম্ভব না। আমরা পাসওয়ার্ড যেভাবে হ্যাশ করেছিলাম সেটা কখনোই বের করা সম্ভব না। কিন্তু এক্ষেত্রে ডাটা এনকোডেড থাকে। + +এবার আমরা JWT জেনারেট করবো। এর জন্য কিছু [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken) নামে একটা প্যাকেজ আছে। এটাকে আমরা আমাদের অ্যাপ্লিকেশনে ইনস্টল করে নিবো। এরপর আমরা আমাদের সার্ভারে সেটা ইমপোর্ট করে নিবো। + +এবার আমরা টোকেন জেনারেট করবো। একটা টোকেন জেনারেট করতে হলে প্রথমে jwt.sign() মেথডটা আমরা নিবো। এবং আর্গুমেন্ট আকারা পেলোড পাস করার জন্য আমরা ইউজারের যে যে ইনফরমেশনগুলো সিক্রেট না সে সে ইনফরমেশনগুলো দিয়ে দিতে পারি। আমরা এখানে পাস করবো `user._doc`। কারণ আমরা গত ক্লাসে দেখেছিলাম user এর সমস্ত ডাটা স্টোর হয় `_doc` এর মধ্যে। সেকেন্ড আর্গুমেন্ট আকারে আমাদের jwt.Secret এই কী ব্যবহার করতে হবে। এখানে যে সিক্রেট কী ব্যবহার করা হবে সেটা পরবর্তীতে ভেরিফাই করার কাজেও ব্যবহার করা যাবে। এটা খুবই গুরুত্বপূর্ণ। যদি এই সিক্রেট কী কোনো কারণে এক্সপোজ হয়ে যায় তাহলে যে কেউ আমাদের সিস্টেমের এক্সেস নিয়ে নিতে পারবে। যে কেউ একটা টোকেন অ্যাসাইন করতে পারবে আর যে কেউ ঐ টোকেন ব্যবহার করে লগইন করে ফেলতে পারবে। তাই এই কী সবচেয়ে সিকিউর ওয়েতে রাখতে হয়। যদিও আমরা এখন সবচেয়ে ইনসিকিউর ওয়েতে এই কী রাখবো। এরপর থার্ড আর্গুমেন্ট আকারে আমরা একটা অবজেক্ট পাস করতে পারি যার মধ্যে বিভিন্ন অপশন আছে। আমরা চাইলে আমাদের পছন্দমতো অ্যালগরিদম চেইঞ্জ করে দিতে পারি। এখানে ডিফল্ট হিসেবে HS256 অ্যালগরিদম ইউজ করা হয়, যেটা খুবই স্ট্রং, তাই আমাদের এটা নিয়ে চিন্তা করার দরকার নেই। আমাদের এখন এতকিছু কাস্টমাইজ করার দরকার নেই। আমরা ডিফল্ট যে সিস্টেম সেটাই রাখবো। **তবে ভবিষ্যতের জন্য একটা নোট রাখতে পারেন, যে RS দিয়ে যে অ্যালগরিদমগুলো আছে সেগুলো আপনারা ব্যবহার করবেন না। এতে কিছু ইস্যু তৈরি হয়েছিল। এগুলো খুব একটা ভাল না।** + +```js +const express = require('express'); +const connectDB = require('./db'); +const bcrypt = require('bcryptjs'); +const jwt = require('jsonwebtoken'); +const User = require('./models/User'); + +const app = express(); + +app.use(express.json()); + +app.post('/register', async (req, res, next) => { + const { name, email, password } = req.body; + if (!name || !email || !password) { + return res.status(400).json({ message: 'Invalid Data' }); + } + try { + let user = await User.findOne({ email }); + if (user) { + return res.status(400).json({ message: 'User already exists' }); + } + user = new User({ name, email, password }); + const salt = await bcrypt.genSalt(10); + const hash = await bcrypt.hash(password, salt); + user.password = hash; + await user.save(); + return res.status(201).json({ message: 'User Created Successfully', user }); + } catch (error) { + next(error); + } +}); + +app.post('/login', async (req, res, next) => { + const { email, password } = req.body; + try { + const user = await User.findOne({ email }); + + if (!user) { + return res.status(400).json({ message: 'Invalid Credential' }); + } + + const isMatch = await bcrypt.compare(password, user.password); + if (!isMatch) { + return res.status(400).json({ message: 'Invalid Credential' }); + } + + delete user._doc.password; + + const token = jwt.sign(user._doc, 'secret-key'); + + return res.status(200).json({ message: 'Login Successful', token }); + } catch (e) { + next(e); + } +}); + +app.get('/', (_req, res) => { + const obj = { + name: 'Ayman', + email: 'ayman@example.com', + }; + res.json(obj); +}); + +app.use((err, _req, res, _next) => { + console.log(err); + res.status(500).json({ message: 'Server Error Occurred' }); +}); + +connectDB('mongodb://localhost:27017/attendance-db') + .then(() => { + console.log('Database Connected'); + app.listen(4000, () => { + console.log("I'm listening on port 4000"); + }); + }) + .catch((e) => console.log(e)); +``` + +এই যে এতটুকু কোড লিখলাম সে আমাদের জন্য একটা টোকেন জেনারেট করে দিবে। বেসিক্যালি আমাদের দরকার এখন এই টোকেনটা। আমরা রেসপন্স ম্যাসেজের মধ্যে user অবজেক্ট না পাঠিয়ে এর মধ্যে token দিয়ে দিবো। আমাদের login এর যতটুকু কাজ বাকি ছিল ততটুকু কাজ কমপ্লিট। এখন এই কাজটা করার পরে কি ঘটছে একটু দেখে আসি চলুন। প্রথমে সার্ভারটা রান করে নিতে হবে। এরপর পোস্টম্যান বা thunder client এ আমাদের রিকোয়েস্ট পাঠাবো। দেখবো আগের বার আমরা যে ইউজার অবজেক্ট পেয়েছিলাম তার জায়গায় হিজিবিজি লেখা কিছু এসেছে token প্রোপার্টির মধ্যে। এই হিজিবিজি লেখাই হলো আমাদের জেনারেটেড টোকেন। + +![token](./images/token-1.png) + +এখন এই টোকেনটা কিন্তু মোটেও সিকিউর না। আমরা এই টোকেনটা কপি করে যদি আমরা [jwt.io](https://jwt.io/) তে গিয়ে যদি পেস্ট করি তাহলে আমরা যে যে ইনফরমেশন দিয়েছিলাম সব পেয়ে যাবো। + +![decode](./images/decode.png) + +দেখুন আমাদের পুরো ইউজার অবজেক্ট দেখাচ্ছে। পাসওয়ার্ড নেই কারণ আমরা টোকেন জেনারেট করার পূর্বে পাসওয়ার্ড ডিলিট করে দিয়েছি। ডাটাগুলো এখানে অ্যাভেইলেবল তার মানে এটা না যে অন্য কেউ অন্য কোনো সার্ভার থেকে এই ডাটাগুলো ভেরিফাই করতে পারবে। ততক্ষণ করতে পারবে না, যতক্ষণ পর্যন্ত তার সাথে আমাদের দেয়া sign মেথডের মধ্যে সিক্রেট কী ম্যাচ না করবে। এবার আমরা এই টোকেন নিয়ে কিছু কাজ করতে পারি। কোথায় করতে পারি? ঐ যে থার্ড আর্গুমেন্টের অবজেক্টে। আমরা আপাতত শুধু expiresin নিয়ে কাজ করবো। আমরা বলে দিলাম শুধু ২ ঘন্টার জন্য একটা টোকেন জেনারেট করবে। + +```js +const express = require('express'); +const connectDB = require('./db'); +const bcrypt = require('bcryptjs'); +const jwt = require('jsonwebtoken'); +const User = require('./models/User'); + +const app = express(); + +app.use(express.json()); + +app.post('/register', async (req, res, next) => { + const { name, email, password } = req.body; + if (!name || !email || !password) { + return res.status(400).json({ message: 'Invalid Data' }); + } + try { + let user = await User.findOne({ email }); + if (user) { + return res.status(400).json({ message: 'User already exists' }); + } + user = new User({ name, email, password }); + const salt = await bcrypt.genSalt(10); + const hash = await bcrypt.hash(password, salt); + user.password = hash; + await user.save(); + return res.status(201).json({ message: 'User Created Successfully', user }); + } catch (error) { + next(error); + } +}); + +app.post('/login', async (req, res, next) => { + const { email, password } = req.body; + try { + const user = await User.findOne({ email }); + + if (!user) { + return res.status(400).json({ message: 'Invalid Credential' }); + } + + const isMatch = await bcrypt.compare(password, user.password); + if (!isMatch) { + return res.status(400).json({ message: 'Invalid Credential' }); + } + + delete user._doc.password; + + const token = jwt.sign(user._doc, 'secret-key', { expiresIn: '2h' }); + + return res.status(200).json({ message: 'Login Successful', token }); + } catch (e) { + next(e); + } +}); + +app.get('/', (_req, res) => { + const obj = { + name: 'Ayman', + email: 'ayman@example.com', + }; + res.json(obj); +}); + +app.use((err, _req, res, _next) => { + console.log(err); + res.status(500).json({ message: 'Server Error Occurred' }); +}); + +connectDB('mongodb://localhost:27017/attendance-db') + .then(() => { + console.log('Database Connected'); + app.listen(4000, () => { + console.log("I'm listening on port 4000"); + }); + }) + .catch((e) => console.log(e)); +``` + +এবার যদি আবার লগইন করি তাহলে দেখবো আরেকটা নতুন টোকেন জেনারেট হয়েছে। যতবার লগইন করবো ততবার একটা টোকেন জেনারেট করবে। আগেরটার সাথে কোনো মিল থাকব না। এবার এটা ডিকোড করলে দেখবো আরেকটা এক্সট্রা প্রোপার্টি এসেছে। সেটা হলো "exp"। মানে কতক্ষণ পর সেটা এক্সপায়ার হয়ে যাবে সেটা। + +![decode](./images/decode-1.png) + +যদিও টাইমটা বুঝা যাচ্ছে না। আপনি যদি কনসোলে গিয়ে `new Date(1656580042 * 1000)` লিখেন তাহলে দেখবেন কখন এক্সপায়ার হবে সেটা আপনাকে দিয়ে দিবে। ১০০০ দিয়ে গুণ করার কারণ হলো এই টাইমটা আছে সেকেন্ডে। আমাদের Date অবজেক্ট কাউন্ট করে মিলিসেকেন্ডে। মিলিসেকেন্ডে নেয়ার জন্য আমরা ১০০০ দিয়ে গুণ করে নিলাম। + +![date](./images/date.png) + +এবার আমরা JWT নিয়ে আলোচনা করি। আপনি যদি [JWT Introduction](https://jwt.io/introduction) এই লিংকে যান তাহলে দেখবেন JWT নিয়ে একটা ওভারভিউ দেয়া হয়েছে। এখানে দেখা যাচ্ছে অথোরাইজেশন আর ইনফরমেশন এক্সচেইঞ্জের কাজে ব্যবহার করা হয়। এর প্রথমে থাকে Header যেটা দিয়ে আমরা বুঝতে পারি এখানে কোন অ্যালগরিদম ইউজ করা হয়েছে, এরপর আছে Payload যেটা হচ্ছে আমরা আমাদের কোডে যেই user এর একটা বেসিক ইনফরমেশন রেখেছিলাম ওরকম ইনফরমেশন যেটা দিয়ে ফ্রন্টএন্ড ডেভেলপার ইনফরমেশন বের করে নিতে পারবে, এরপর আছে Signature যেটা খুব সিকিউর আর যেটা নিয়ে আমাদের কোনো কাজ নেই। আমাদের কাজ মূলত পেলোড নিয়ে। আপনারা একটু [JWT Introduction](https://jwt.io/introduction) লিংকে গিয়ে ভালভাবে পড়ে নিবেন। ডিটেইলস বলা হয়েছে এখানে। + +এবার আমরা একটা সিক্রেট বা প্রাইভেট রাউট তৈরি করতে চাইছি দেখার জন্য যে লগইন সিস্টেমটা কিভাবে কাজ করে। আমরা একটা get রাউট বানাবো আমাদের সার্ভারে লগইন রাউটের নিচে এবং '/' রাউটের আগে। এখানে আর পুরো ফাইল দেখাচ্ছি না। আপনারা সোর্স কোডের লিংক এই আর্টিকেলের শেষ পেয়ে যাবেন। + +```js +// server.js + +app.get('/private', (req, res) => { + return res.status(200).json({ message: 'I am a private route' }); +}); +``` + +যদি আমরা ব্রাউজারে গিয়ে `localhost:4000/private' লিখে সার্চ দিই তাহলে দেখা যাবে এই ম্যাসেজটা আসবে। কিন্তু এটা তো আসার কথা না। যেহেতু আমাদের সিস্টেম প্রাইভেট করা। এবার আমরা একটা পাব্লিক রাউট ক্রিয়েট করে দেখি। আমরা বুঝার চেষ্টা করি প্রাইভেট আর পাব্লিক রাউট কিভাবে কাজ করে। + +```js +app.get('/private', (req, res) => { + return res.status(200).json({ message: 'I am a private route' }); +}); + +app.get('/public', (req, res) => { + return res.status(200).json({ message: 'I am a public route' }); +}); +``` + +যদি রিকোয়েস্ট পাঠাই এই ম্যাসেজটা দিবে। এখন একটা রাউটকে আমরা কিভাবে প্রাইভেট বানাতে পারি? যদি সেই রাউটের হেডারের মধ্যে রিকোয়েস্টের মধ্যে আমাদের টোকেনটা না আসে সেই রাউটকে আমরা এক্সেস নিতে দিবো না। তাকে আমরা রিটার্ন স্টেটমেন্ট পর্যন্ত আসতেই দিবো না। গত ক্লাসে আমরা দেখেছিলাম রিকোয়েস্ট থেকে ডাটা অনেকভাবে আসতে পারে। তার একটা সিস্টেম হলো রিকোয়েস্ট বডি। এখন get রিকোয়েস্টে কিভাবে বডি পাবে, get রিকোয়েস্টে তো বডিই থাকে না। এখানে সমাধান হচ্ছে রিকোয়েস্ট হেডার। আমরা একটু কনসোলে লগ করে দেখি হেডারে কি আছে। + +```js +app.get('/private', (req, res) => { + console.log(req.headers); + return res.status(200).json({ message: 'I am a private route' }); +}); +``` + +দেখবো একটা অবজেক্ট এসেছে। + +![header](./images/header.png) + +এখন এই অবজেক্ট আসলো কোথা থেকে? আমরা তো এটা পাঠাই নি। এগুলো ব্রাউজার থেকে এসেছে। আপনি যদি ইনস্পেক্টে গিয়ে নেটওয়ার্ক ট্যাবে যান, সেখানে হেডার হিসেবে এগুলোই দেখবেন। এখন হেডার হিসেবে আমাদের টোকেনটা পাস করতে হবে। তবে এই মুহূর্তে আমরা ব্রাউজার থেকে পারবো না। আমরা পোস্টম্যান থেকে পারবো। ব্রাউজার থেকে যখন আমাদের অ্যাপ্লিকেশন রেডি হয়ে যাবে তখন আমরা পাঠাতে পারবো। JWT এর ক্ষেত্রে এটা থাকবে হেডারের authorization কী তে। আমরা একটু এটা লগ করে দেখি। + +```js +app.get('/private', (req, res) => { + console.log(req.headers.authorization); + return res.status(200).json({ message: 'I am a private route' }); +}); +``` + +আমরা দেখবো এখানে undefined রিটার্ন করছে। এখন যদি undefined হয় তাহলে আমরা নিচে আর যেতে দিবো না। এবার একটা কন্ডিশন লিখি। + +```js +app.get('/private', (req, res) => { + if (!req.headers.authorization) { + return res.status(401).json({ message: 'Unauthorized' }); + } + return res.status(200).json({ message: 'I am a private route' }); +}); +``` + +এবার আপনি আর প্রাইভেট রাউটে ঢুকতেই পারবেন না। দেখবেন ঢুকতে গেলে আপনাকে `{ message: 'Unauthorized' }` এটা দিবে। + +![pvt](./images/pvt.png) + +এখন যদি আমরা এখানে টোকেনটা দিয়ে দিই তাহলে কী হবে? এটা আমাদের দিতে হবে হেডারে গিয়ে Authorization নামক প্রোপার্টির মধ্যে। + +![auth](./images/auth.png) + +এখন দেখেন Authorization এ টোকেন দেয়াতে এটা আবার প্রাইভেট রাউটের এক্সেস পাচ্ছে। যদিও আমরা এখনও ব্যাকএন্ডে ভেরিফাই করিনি। আমরা জাস্ট এতটুকু করেছি যদি টোকেন না পায় তাহলে সে আর নিচে যাবে না। + +আরেকটা উপায় আছে। যেটা আসলে ফরমাল ওয়ে এবং যে উপায়ে আমরা সাধারণত কাজ করে থাকি। সেটা হলো Header এ না গিয়ে Authorization এ গিয়ে bearer token হিসেবে দিয়ে দেয়া। + +![bearer](./images/bearer.png) + +```js +app.get('/private', (req, res) => { + const token = req.headers.authorization; + + if (!token) { + return res.status(401).json({ message: 'Unauthorized' }); + } + console.log(token); + return res.status(200).json({ message: 'I am a private route' }); +}); +``` + +যদি আমরা এভাবে টোকেনটা প্রিন্ট করে দেখি দেখবো কনসোলে টোকেনের আগে Bearer নামে একটা লেখা এসেছে আর মাঝে এক স্পেস আছে। এটা কনভেনশন। এখন আমাদের জন্য এই কাজ করছে পোস্টম্যান বা থান্ডার ক্লায়েন্ট। কিন্তু আমরা যখন ফ্রন্টএন্ডে কাজ করবো তখন নিজেরা লাগিয়ে দিবো। এটা কনভেনশন। লাগালে বা না লাগালে কোনো অসুবিধা হয়না। + +এতক্ষণ আমরা কাজ করেছি যদি টোকেন পাওয়া না যায় তা নিয়ে। এখন যদি পাওয়া যায় তাহলে সেটাকে ভেরিফাই করে দেখতে হবে সেই টোকেনটা আমাদের জেনারেট করা টোকেন কিনা। সেটার জন্য jsonwebtoken প্যাকেজে verify নামক একটা মেথড আছে। সেটা ব্যবহার করে আমরা এটা ভেরিফাই করবো। প্রথমে আমাদের টোকেন থেকে Bearer কথাটা বাদ দিতে হবে। এরপর ঐ টোকেন ভেরিফাই করবো। নাহয় এরর দেখাবে। + +```js +app.get('/private', (req, res) => { + let token = req.headers.authorization; + + if (!token) { + return res.status(401).json({ message: 'Unauthorized' }); + } + + try { + token = token.split(' ')[1]; + const user = jwt.verify(token, 'secret-key'); + console.log(user); + } catch (e) { + return res.status(400).json({ message: 'Invalid Token' }); + } + + return res.status(200).json({ message: 'I am a private route' }); +}); +``` + +যদি টোকেন ঠিক থাকে তাহলে নিচের রেসপন্স দিবে। + +![tkn1](./images/tkn-1.png) + +যদি টোকেন ভুল হয় তাহলে নিচের রেসপন্স দিবে। + +![tkn2](./images/tkn-2.png) + +এখন এখানে আরেকটা সমস্যা আছে। সেটা হলো যে কেউ কোনোভাবে যদি আমাদের সিক্রেট কী পেয়ে যায় তাহলে তো সে টোকেন জেনারেট করে ফেলতে পারবে। এখন টোকেন জেনারেট করলেই তো হবে না। ইউজারের আইডি ম্যাচ করছে কিনা সেটাও দেখা দরকার। আমরা আইডি দিয়ে খুঁজার চেষ্টা করি ডাটাবেজে ঐ আইডির ইউজার সত্যিই আছে কিনা। + +```js +app.get('/private', async (req, res) => { + let token = req.headers.authorization; + + if (!token) { + return res.status(401).json({ message: 'Unauthorized' }); + } + + try { + token = token.split(' ')[1]; + const decoded = jwt.verify(token, 'secret-key'); + const user = await User.findById(decoded._id); + + if (!user) { + return res.status(401).json({ message: 'Unauthorized' }); + } + + return res.status(200).json({ message: 'I am a private route' }); + } catch (e) { + return res.status(400).json({ message: 'Invalid Token' }); + } +}); +``` + +পাবলিক রাউটের ক্ষেত্রে কোনো টোকেন নাই কোনো ঝামেলা নাই। + +এখন আমরা যদি আমাদের রিকোয়ারমেন্ট অ্যানালাইসিসটা একটু দেখি, তাহলে দেখা যাবে ওখানে ম্যাক্সিমাম রাউটই প্রাইভেট রাউট। এখন প্রতিটার জন্য যদি আমরা প্রাইভেট রাউট বানাতে যায় তাহলে কত কোডের পুনরাবৃত্তি হবে বুঝতে পারছেন? আর কোড পুনরাবৃত্তিও ব্যাপার না। ব্যাপার হলো যদি কোনো ভুল হয় বা আপডেট আসে তখন কি হবে। এই সমস্যা থেকে মুক্তির জন্য আমরা একটা কন্ট্রোলার বানাবো যাকে আমরা বলি মিডলওয়্যার। কন্ট্রোলার আর মিডলওয়্যারের মধ্যে একটা বেসিক পার্থক্য হচ্ছে মিডলওয়্যার যদি অথোরাইজড ডাটা পায় তাহলেই কন্ট্রোলারের কাছে পাঠাবে। আর যদি না পায় তাহলে ওখানে থেকেই সোজা ফেরত পাঠিয়ে দিবে। এখন আমরা আমাদের server ফোল্ডারে middleware নামে একটা ফোল্ডার ক্রিয়েট করবো। এই ফোল্ডারে আমরা authenticate.js নামে একটা ফাইল ক্রিয়েট করবো। করে এর মধ্যে আমরা একটা মিডলওয়্যার ফাংশন তৈরি করবো। এবং এটা আমরা এক্সপোর্ট করে দিবো। + +```js +function authenticate(req, res, next) {} + +module.exports = authenticate; +``` + +এবার আমরা আমাদের সার্ভার থেকে প্রাইভেট রাউটে যে ফাংশনটা লিখেছিলাম সেটা কাট করে নিয়ে আসবো আমাদের মিডলওয়্যার ফাংশনের মধ্যে। শুধু একটা লাইন আমরা আমাদের প্রাইভেট রাউটে রাখবো সেটা হলো `return res.status(200).json({ message: 'I am a private route' });`. + +```js +// authenticate.js + +const jwt = require('jsonwebtoken'); +const User = require('../models/User'); + +async function authenticate(req, res, next) { + try { + let token = req.headers.authorization; + + if (!token) { + return res.status(401).json({ message: 'Unauthorized' }); + } + token = token.split(' ')[1]; + const decoded = jwt.verify(token, 'secret-key'); + const user = await User.findById(decoded._id); + + if (!user) { + return res.status(401).json({ message: 'Unauthorized' }); + } + + req.user = user; + next(); + } catch (e) { + return res.status(400).json({ message: 'Invalid Token' }); + } +} + +module.exports = authenticate; +``` + +এখানে আমরা যে ইউজারটা বের করে এনেছি সেটা কন্ট্রোলারকে কিভাবে বুঝাবো? কন্ট্রোলার কিভাবে বুঝবে এই রিকোয়েস্টে ইউজার অবজেক্ট আছে। req অবজেক্ট একটা মিউটেবল অবজেক্ট। আপনি যেকোনো প্রোপার্টি এতে অ্যাড করতে পারবেন। আমরা তাই এর মধ্যে ইউজারকে রেখে দিলাম। শেষে next() কল করে দিলাম। + +এবার আমরা আমাদের সার্ভারে এই মিডলওয়্যার ব্যবহার করবো। + +```js +// server.js +const authenticate = require('./middleware/authenticate'); + +app.get('/private', authenticate, async (req, res) => { + console.log('I am authenticated', req.user); + return res.status(200).json({ message: 'I am a private route' }); +}); +``` + +এবার রিকোয়েস্ট পাঠিয়ে আপনারা চেক করতে পারবেন। দেখবেন কাজ করছে। + +এবার যদি টোকেন এক্সপায়ার হয়ে যায় তাহলে কি হবে। আপনার টাইম কমিয়ে দিয়ে চেক করে দেখতে পারেন। যদি এক্সপায়ার হয়ে যায় ইনভ্যালিড টোকেন দেখাবে। কাজ করবে না। + +এবার আমরা চাইলে হাজার হাজার প্রাইভেট রাউট বানাতে পারি এই মিডলওয়্যার দিয়ে। + +আমাদের অ্যাপ্লিকেশনের সবচেয়ে কঠিন কাজের মধ্যে অন্যতম কাজ শেষ। + +আমরা অনেক রাউট নিয়ে কাজ করবো সব যদি এক ফাইলে থাকে সেটা পরে মেইনটেইন করতে খবর হয়ে যাবে। আমরা এখন কোড রিফ্র্যাক্টর করতে হবে। + +প্রথমে আমরা routes নামের একটা ডিরেক্টরি বানিয়ে ফেলবো। এরপর বানাবো controller ডিরেক্টরি। এরপর ক্রিয়েট করবো service ডিরেক্টরি। + +প্রথমে আমরা আমাদের কন্ট্রোলারগুলোকে আলাদা করে ফেলি। কন্ট্রোলার হলো রাউটের ভিতরে যে ফাংশনগুলো লিখেছি শুধু সেগুলো। এখন controller ডিরেক্টরিতে auth.js নামের একটা ফাইল বানাই। এখানে রেজিস্টার আর লগইনের রাউটের ফাংশন কাট করে নিয়ে লিখে এক্সপোর্ট করে দিবো। + +```js +// auth.js + +const User = require('../models/User'); +const bcrypt = require('bcryptjs'); +const jwt = require('jsonwebtoken'); + +const registerController = async (req, res, next) => { + const { name, email, password } = req.body; + if (!name || !email || !password) { + return res.status(400).json({ message: 'Invalid Data' }); + } + try { + let user = await User.findOne({ email }); + if (user) { + return res.status(400).json({ message: 'User already exists' }); + } + user = new User({ name, email, password }); + const salt = await bcrypt.genSalt(10); + const hash = await bcrypt.hash(password, salt); + user.password = hash; + await user.save(); + return res.status(201).json({ message: 'User Created Successfully', user }); + } catch (error) { + next(error); + } +}; + +const loginController = async (req, res, next) => { + const { email, password } = req.body; + try { + const user = await User.findOne({ email }); + + if (!user) { + return res.status(400).json({ message: 'Invalid Credential' }); + } + + const isMatch = await bcrypt.compare(password, user.password); + if (!isMatch) { + return res.status(400).json({ message: 'Invalid Credential' }); + } + + delete user._doc.password; + + const token = jwt.sign(user._doc, 'secret-key', { expiresIn: '2h' }); + + return res.status(200).json({ message: 'Login Successful', token }); + } catch (e) { + next(e); + } +}; + +module.exports = { registerController, loginController }; +``` + +এবার এই কন্ট্রোলার আমরা সার্ভারে ব্যবহার করবো। + +```js +// server.js +const { registerController, loginController } = require('./controller/auth'); + +app.post('/register', registerController); + +app.post('/login', loginController); +``` + +যদি অ্যাপ্লিকেশন কোনো এরর ছাড়া রানিং থাকে তাহলে আমাদের রিফ্র্যাক্টর সঠিক হয়েছে বলে ধরে নেয়া যায়। এখন আমরা রাউটগুলোকে routes ফোল্ডারের মধ্যে রাখতে চাইছি। + +আমরা রাউটস ফোল্ডারে দুইটা ফাইল ক্রিয়েট করবো auth.js, index.js নামে। auth.js এ আমাদের অথেনটিকেশনের রাউটগুলো থাকবে। + +```js +// routes/auth.js + +const router = require('express').Router(); +const { registerController, loginController } = require('../controller/auth'); + +router.post('/register', registerController); +router.post('/login', loginController); + +module.exports = router; +``` + +আপনারা খেয়াল করলে দেখবেন আমাদের রাউটিং সিস্টেম খুব বাজে। ডোমেইনের নামের সাথেই রাউটস। বড় অ্যাপ্লিকেশনে এমন করা হয় না। Prefixing করা হয়। আমরা /api/v1/auth/register, /api/v1/auth/login এভাবে লিখতে পারি। কিন্তু যদি এক হাজারটা রাউটস হয় তাহলে সমস্যা হয়ে যাবে। তাই আমরা এই প্রিফিক্সিং এর জন্য রাউটস ফোল্ডারে যে ইনডেক্স ফাইল নিয়েছিলা ওখানে একটা রাউট বানিয়ে নিবো। + +```js +// routes/index.js + +const router = require('express').Router(); +const authRoutes = require('./auth'); + +router.use('/api/v1/auth', authRoutes); + +module.exports = router; +``` + +আমাদের সার্ভারে এই রাউট ইমপোর্ট করি। + +```js +const express = require('express'); +const connectDB = require('./db'); +const authenticate = require('./middleware/authenticate'); +const routes = require('./routes'); + +const app = express(); + +app.use(express.json()); + +app.use(routes); +``` + +এবার আমাদের রাউট লিখতে হবে নিচের ছবির মতো। আমরা টেস্ট করলাম আমাদের সব ঠিক আছে কিনা। যেহেতু লগইন করা যাচ্ছে তাহলে আমাদের সব ঠিকই আছে। + +![v1](./images/v1.png) + +এখন আমরা আমাদের কন্ট্রোলার ফাংশন যেভাবে লিখেছি সেটা আমাদের কন্ট্রোলারের উদ্দেশ্য না। কন্ট্রোলার শুধু রিকোয়েস্ট নিবে, তার হয়ে অন্য কেউ সেটা হ্যান্ডেল করবে, করার পর সে রেস্পন্স ব্যাক করবে। বিজনেস লজিক নিয়ে কাজ করা কন্ট্রোলারের কাজ না। তাই আমরা এগুলোকে কন্ট্রোলার থেকে বের করে আনবো। আমরা চলে যাব service ফোল্ডারে। সেখানে auth.js, user.js নামে দুইটা ফাইল ক্রিয়েট করবো। auth.js এ আমরা auth কন্ট্রোলারের বিষয়গুলো রাখবো। আর যেহেতু ইউজারের ডাটা আপডেটের বিষয়বস্তু আছে সেগুলো আমরা user.js এর মধ্যে রাখবো। + +```js +// service/auth.js + +const User = require('../models/User'); +const bcrypt = require('bcryptjs'); +const jwt = require('jsonwebtoken'); + +const registerService = async ({ name, email, password }) => { + let user = await User.findOne({ email }); + if (user) { + return res.status(400).json({ message: 'User already exists' }); + } + user = new User({ name, email, password }); + const salt = await bcrypt.genSalt(10); + const hash = await bcrypt.hash(password, salt); + user.password = hash; + await user.save(); +}; + +const loginService = async ({ email, password }) => { + const user = await User.findOne({ email }); + + if (!user) { + return res.status(400).json({ message: 'Invalid Credential' }); + } + + const isMatch = await bcrypt.compare(password, user.password); + if (!isMatch) { + return res.status(400).json({ message: 'Invalid Credential' }); + } + + delete user._doc.password; + + const token = jwt.sign(user._doc, 'secret-key', { expiresIn: '2h' }); +}; + +module.exports = { registerService, loginService }; +``` + +এবার আমরা আমাদের auth কন্ট্রোলারকে ক্লিন করি। একটা জিনিস মনে রাখতে হবে আমরা যে কন্ট্রোলার নিয়ে কাজ করবো শুধু সেই সার্ভিস ব্যবহার করবো। অন্য সার্ভিস ব্যবহার করবো না। এখানে auth কন্ট্রোলার নিয়ে কাজ করছি তাই auth সার্ভিস ব্যবহার করবো, user সার্ভিস না। একটা সার্ভিসে আরেকটা সার্ভিস ব্যবহার করা যেতে পারে সেটা কোনো সমস্যা না। + +এখন আমাদের কন্ট্রোলার অনেক ক্লিন হয়ে গেছে দেখুন। + +```js +// controller/auth.js + +const { registerService, loginService } = require('../service/auth'); + +const registerController = async (req, res, next) => { + const { name, email, password } = req.body; + if (!name || !email || !password) { + return res.status(400).json({ message: 'Invalid Data' }); + } + try { + const user = await registerService({ name, email, password }); + return res.status(201).json({ message: 'User Created Successfully', user }); + } catch (error) { + next(error); + } +}; + +const loginController = async (req, res, next) => { + const { email, password } = req.body; + try { + const token = await loginService({ email, password }); + return res.status(200).json({ message: 'Login Successful', token }); + } catch (e) { + next(e); + } +}; + +module.exports = { registerController, loginController }; +``` + +আমাদের auth সার্ভিসের মধ্যে User মডেলের কিছু কাজ আছে। যেমন ডাটাবেজ থেকে ইউজার বের করে আনা। এগুলো তো ইউজারের কাজ। কেন auth এর মধ্যে থাকবে? সুতরাং auth থেকে আমাদের ইউজারকে বের করে এই রিলেটেড কাজগুলো user.js এর মধ্যে লিখবো। আমরা ইউজার খুঁজে বের করার একটা ফাংশন এবং নতুন ইউজার তৈরি করার জন্য একটা ফাংশন লিখবো user.js এর মধ্যে। + +```js +// service/user.js + +const User = require('../models/User'); + +const findUserByProperty = (key, value) => { + if (key === '_id') { + return User.findById(value); + } + return User.findOne({ [key]: value }); +}; + +const createNewUser = ({ name, email, password }) => { + const user = new User({ name, email, password }); + return user.save(); +}; + +module.exports = { + findUserByProperty, + createNewUser, +}; +``` + +এবার আমরা আমাদের auth সার্ভিসের মধ্যে রেজিস্টার সার্ভিসটা রিফ্র্যাক্টর করে ফেলি। + +```js +// service/auth.js + +const User = require('../models/User'); +const bcrypt = require('bcryptjs'); +const jwt = require('jsonwebtoken'); +const { findUserByProperty, createNewUser } = require('./user'); + +const registerService = async ({ name, email, password }) => { + let user = await findUserByProperty('email', email); + if (user) { + const error = new Error('User already exists'); + error.status(400); + throw error; + } + + const salt = await bcrypt.genSalt(10); + const hash = await bcrypt.hash(password, salt); + return createNewUser({ name, email, password: hash }); +}; + +module.exports = { registerService }; +``` + +এবার দেখি আমাদের রিফ্র্যাক্টর করা কোড কাজ করছে কিনা। + +![new-reg](./images/new-reg.png) +![new-db](./images/new-db.png) + +কোড কাজও করছে আবার ডাটাবেজেও সেইভ হয়েছে। + +এবার আমরা লগইন সার্ভিস নিয়ে কাজ করি একটু। + +```js +// /service/auth.js + +const bcrypt = require('bcryptjs'); +const jwt = require('jsonwebtoken'); +const { findUserByProperty, createNewUser } = require('./user'); + +const registerService = async ({ name, email, password }) => { + let user = await findUserByProperty('email', email); + if (user) { + const error = new Error('User already exists'); + error.status(400); + throw error; + } + + const salt = await bcrypt.genSalt(10); + const hash = await bcrypt.hash(password, salt); + return createNewUser({ name, email, password: hash }); +}; + +const loginService = async ({ email, password }) => { + const user = await findUserByProperty('email', email); + + if (!user) { + const error = new Error('Invalid Credential'); + error.status(400); + throw error; + } + + const isMatch = await bcrypt.compare(password, user.password); + if (!isMatch) { + const error = new Error('Invalid Credential'); + error.status(400); + throw error; + } + + const payload = { + _id: user._id, + name: user.name, + email: user.email, + roles: user.roles, + accountStatus: user.accountStatus, + }; + + return jwt.sign(payload, 'secret-key', { expiresIn: '2h' }); +}; + +module.exports = { registerService, loginService }; +``` + +এবার আমাদের কোড কাজ করে কিনা দেখি একটু। + +![new-login](./images/new-login.png) + +পারফেক্টলি কাজ করছে। + +কিন্তু আমাদের auth সার্ভিসে এরর হ্যান্ডেলিং এর জন্য সবসময় একই কোড লিখতে হয়েছে। সেটার জন্য আমরা সিম্পলি একটা ইউটিলিটি ফাংশন তৈরি করতে পারি। আমরা utils নামে একটা ফোল্ডার ক্রিয়েট করবো। সেখানে error.js নামে একটা ফাইল ক্রিয়েট করবো। এরপর সেখানে আমরা আমাদের এরর হ্যান্ডলিং এর কোড লিখবো। + +```js +// /utils/error.js + +function error(msg = 'Something Went Wrong', status = 500) { + const e = new Error(msg); + e.status = status; + return e; +} + +module.exports = error; +``` + +এবার আমাদের auth সার্ভিস এ আমরা এটা ব্যবহার করবো। + +```js +// /service/auth.js + +const bcrypt = require('bcryptjs'); +const jwt = require('jsonwebtoken'); +const error = require('../utils/error'); +const { findUserByProperty, createNewUser } = require('./user'); + +const registerService = async ({ name, email, password }) => { + let user = await findUserByProperty('email', email); + if (user) { + throw error('User already exists', 400); + } + + const salt = await bcrypt.genSalt(10); + const hash = await bcrypt.hash(password, salt); + return createNewUser({ name, email, password: hash }); +}; + +const loginService = async ({ email, password }) => { + const user = await findUserByProperty('email', email); + + if (!user) { + throw error('Invalid Credential', 400); + } + + const isMatch = await bcrypt.compare(password, user.password); + if (!isMatch) { + throw error('Invalid Credential', 400); + } + + const payload = { + _id: user._id, + name: user.name, + email: user.email, + roles: user.roles, + accountStatus: user.accountStatus, + }; + + return jwt.sign(payload, 'secret-key', { expiresIn: '2h' }); +}; + +module.exports = { registerService, loginService }; +``` + +আমরা সার্ভারের মধ্যে যে গ্লোবার এরর হ্যান্ডলার নিয়েছিলাম সেটাতে একটু কাজ করবো। + +```js +// server.js + +app.use((err, _req, res, _next) => { + console.log(err); + const message = err.message ? err.message : 'Server Error Occurred'; + const status = err.status ? err.status : 500; + + res.status(status).json({ + message, + }); +}); +``` + +এবার আমাদের কোডটা ১০০% রিইউজেবল। খুব সুন্দর করে সাজিয়ে গুছিয়ে লেখা হয়েছে। কোনো এরর হলে খুব সহজেই বের করে আনা যাবে কোথায় এরর হয়েছে। + +## Source Code + +এই লেকচারের সোর্স কোডসমূহ এই [লিংক](../../src/lecture-23/) এ পাবেন। + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/full-stack-army/class-overview/Lecture-23/images/auth.png b/full-stack-army/class-overview/Lecture-23/images/auth.png new file mode 100644 index 0000000..eff78e8 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-23/images/auth.png differ diff --git a/full-stack-army/class-overview/Lecture-23/images/bearer.png b/full-stack-army/class-overview/Lecture-23/images/bearer.png new file mode 100644 index 0000000..c8f3475 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-23/images/bearer.png differ diff --git a/full-stack-army/class-overview/Lecture-23/images/date.png b/full-stack-army/class-overview/Lecture-23/images/date.png new file mode 100644 index 0000000..59f5270 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-23/images/date.png differ diff --git a/full-stack-army/class-overview/Lecture-23/images/decode-1.png b/full-stack-army/class-overview/Lecture-23/images/decode-1.png new file mode 100644 index 0000000..96c65e7 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-23/images/decode-1.png differ diff --git a/full-stack-army/class-overview/Lecture-23/images/decode.png b/full-stack-army/class-overview/Lecture-23/images/decode.png new file mode 100644 index 0000000..d1aba8b Binary files /dev/null and b/full-stack-army/class-overview/Lecture-23/images/decode.png differ diff --git a/full-stack-army/class-overview/Lecture-23/images/header.png b/full-stack-army/class-overview/Lecture-23/images/header.png new file mode 100644 index 0000000..d140d58 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-23/images/header.png differ diff --git a/full-stack-army/class-overview/Lecture-23/images/new-db.png b/full-stack-army/class-overview/Lecture-23/images/new-db.png new file mode 100644 index 0000000..ce25263 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-23/images/new-db.png differ diff --git a/full-stack-army/class-overview/Lecture-23/images/new-login.png b/full-stack-army/class-overview/Lecture-23/images/new-login.png new file mode 100644 index 0000000..5dc8d2b Binary files /dev/null and b/full-stack-army/class-overview/Lecture-23/images/new-login.png differ diff --git a/full-stack-army/class-overview/Lecture-23/images/new-reg.png b/full-stack-army/class-overview/Lecture-23/images/new-reg.png new file mode 100644 index 0000000..fd5907b Binary files /dev/null and b/full-stack-army/class-overview/Lecture-23/images/new-reg.png differ diff --git a/full-stack-army/class-overview/Lecture-23/images/pvt.png b/full-stack-army/class-overview/Lecture-23/images/pvt.png new file mode 100644 index 0000000..dece00b Binary files /dev/null and b/full-stack-army/class-overview/Lecture-23/images/pvt.png differ diff --git a/full-stack-army/class-overview/Lecture-23/images/tkn-1.png b/full-stack-army/class-overview/Lecture-23/images/tkn-1.png new file mode 100644 index 0000000..78bdf9d Binary files /dev/null and b/full-stack-army/class-overview/Lecture-23/images/tkn-1.png differ diff --git a/full-stack-army/class-overview/Lecture-23/images/tkn-2.png b/full-stack-army/class-overview/Lecture-23/images/tkn-2.png new file mode 100644 index 0000000..9422653 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-23/images/tkn-2.png differ diff --git a/full-stack-army/class-overview/Lecture-23/images/token-1.png b/full-stack-army/class-overview/Lecture-23/images/token-1.png new file mode 100644 index 0000000..a7a4894 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-23/images/token-1.png differ diff --git a/full-stack-army/class-overview/Lecture-23/images/v1.png b/full-stack-army/class-overview/Lecture-23/images/v1.png new file mode 100644 index 0000000..b913be9 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-23/images/v1.png differ diff --git a/full-stack-army/class-overview/Lecture-24/README.md b/full-stack-army/class-overview/Lecture-24/README.md new file mode 100644 index 0000000..463738e --- /dev/null +++ b/full-stack-army/class-overview/Lecture-24/README.md @@ -0,0 +1,526 @@ +# Lecture 24 - Implement User CRUD Operations + +আমরা গত ক্লাসে রেজিস্ট্রেশন, লগইন নিয়ে কাজ করেছিলাম, প্রজেক্ট রিফ্র্যাক্টর করেছিলাম, অথেনটিকেশন মিডলওয়্যার বানিয়েছিলাম। আজ আমরা user CRUD নিয়ে কাজ করবো। আমাদের নোশনে user crud এ কি কি আছে একটু দেখা যাক। + +![user-crud](./images/user-crud.png) + +দেখা যাচ্ছে ৫টা endpoint আছে এখানে। এগুলো আমাদের তৈরি করতে হবে। + +- Get Users /users +- Get User by ID /users/userId +- Create User /users +- Update User /users/userId +- Delete User /users/userId + +এটা আমরা নতুন একটা সার্ভিস নিয়ে কাজ করছি যেটার নাম User. প্রথমে আমরা রাউটস তৈরি করবো। আমরা routes ফোল্ডারে গিয়ে users.js নামে একটা ফাইল ক্রিয়েট করবো। করে আমরা আমাদের রাউটারকে এক্সপ্রেস থেকে বের করে আনবো এবং তা এক্সপোর্ট করে দিবো। + +```js +const router = require('express').Router(); + +module.exports = router; +``` + +এখন আমাদের রাউটস লাগবে টোটাল ৫টা। আমরা একে একে সেগুলো তৈরি করবো। প্রথমে আমরা Get Users এর জন্য রাউট তৈরি করবো। আমরা filter, sorting, pagination, select properties এর মাধ্যমে ইউজার বের করে আনতে পারি। আপাতত এগুলো আমাদের টুডু লিস্টে থাকবে। আমাদের রাউট হবে `api/v1/users?sort["by","name"]`, মেথড হবে GET, আর এটা হবে private রাউট। আমরা users.js এ এখন লিখবো। + +```js +const router = require('express').Router(); + +/** + * Get all users, include + * - filter + * - sort + * - pagination + * - select properties + * @method Get + * @route api/v1/users?sort["by","name"] + * @visibility private + */ + +router.get(); + +module.exports = router; +``` + +প্রিফিক্সিং এর জন্য আমরা index.js এ user রাউটকে ইমপোর্ট করে নিচের কোডটি লিখে দিব। + +```js +// routes/index.js + +const router = require('express').Router(); +const authRoutes = require('./auth'); +const usersRoutes = require('./users'); + +router.use('/api/v1/auth', authRoutes); +router.use('/api/v1/users', usersRoutes); + +module.exports = router; +``` + +এবার আমরা ইউজার রাউটে একে একে বাকি রাউটগুলো লিখবো। + +```js +// router/users.js + +const router = require('express').Router(); + +/** + * Get user by id or email + */ + +router.get('/:userId', () => {}); + +/** + * update user by id + * @method PUT + */ + +router.put('/:userId', () => {}); + +/** + * update user by id + * @method PATCH + */ + +router.patch('/:userId', () => {}); + +/** + * Delete user by id + */ + +router.delete('/:userId', () => {}); + +/** + * Get all users, include + * - filter + * - sort + * - pagination + * - select properties + * @method Get + * @route api/v1/users?sort["by","name"] + * @visibility private + */ + +router.get('/', () => {}); + +/** + * Create new user + */ + +router.post('/', () => {}); + +module.exports = router; +``` + +এখানে একটা জিনিস মনে রাখতে হবে লেখার সময় যেটা কমন রাউট মানে যে রাউট সবকিছুর সাথে থাকবে সেটাকে সবসময় নিচে লিখতে হবে। ধরেন আপনি '/users/:userid' এই রাউটে হিট করতে চাচ্ছেন। এখন যদি এটা উপরে না থেকে নিচে থাকে আর '/users' রাউট উপরে থাকে অনেক সময় উপরে এই রাউটটা পেয়ে এর রেসপন্স ব্যাক করে দিতে পারে। তাই '/users' কে সবসময় নিচে লিখতে হয়। put and patch দুইটাই আপডেটের মেথড। put ব্যবহার করে আমরা পুরো এক্সিসটিং ডাটাকে আপডেট করতে পারি। আর patch ব্যবহার করে আংশিক পরিবর্তন করতে পারি। এবার আমাদের দরকার কন্ট্রোলার এবং সার্ভিস। আমরা চলে যাই প্রথমে কন্ট্রোলারের কাছে। সেখানে আমরা users.js নামে একটা ফাইল ক্রিয়েট করবো। + +```js +// controller/users.js + +const User = require('../models/User'); + +const getUsers = (req, res, next) => {}; + +const getUserById = (req, res, next) => {}; + +const postUser = (req, res, next) => {}; + +const putUserById = (req, res, next) => {}; + +const patchUserById = (req, res, next) => {}; + +const deleteUserById = (req, res, next) => {}; + +module.exports = { + getUsers, + getUserById, + postUser, + putUserById, + patchUserById, + deleteUserById, +}; +``` + +এবার একে একে ফাংশনালিটিজগুলো যোগ করবো। ফাংশনালিটিজ যোগ করতে হলে আমাদের ডাটাবেজের সাথে রিলেটেড কিছু কাজকর্ম আছে। সেগুলো কন্ট্রোলার করবে না। আগের ক্লাসে সেটা আলোচনা হয়েছিল। তার জন্য আমাদের একটা সার্ভিস লাগবে। অলরেডি আমাদের ইউজার সার্ভিস নামে একটা সার্ভিস আছে। সেখানে আমরা এই কাজগুলো করতে পারি। প্রথমে ইউজার ফাইন্ড করার একটা ফাংশন লিখে ফেলি। মনে রাখতে হবে প্রতিটা ফাংশন লিখে তা এক্সপোর্ট করতে হবে। + +```js +// service/user.js + +const User = require('../models/User'); + +const findUsers = () => { + return User.find(); +}; + +module.exports = { + findUsers, +}; +``` + +এবার আমরা আমাদের getUsers ফাংশনটি কমপ্লিট করবো। + +```js +const userService = require('../service/user'); + +const getUsers = async (req, res, next) => { + /** + * TODO: filter, sort, pagination, select + */ + + try { + const users = await userService.findUsers(); + return res.status(200).json(users); + } catch (e) { + next(e); + } +}; +``` + +এবার এই কন্ট্রোলারকে আমরা আমাদের রাউটের মধ্যে নিয়ে এসে ব্যবহার করবো। + +```js +// routes/users.js +const usersController = require('../controller/users'); + +/** + * Get all users, include + * - filter + * - sort + * - pagination + * - select properties + * @method Get + * @route api/v1/users?sort["by","name"] + * @visibility private + */ + +router.get('/', usersController.getUsers); + +module.exports = router; +``` + +এবার আমরা দেখবো আমাদের এই ফাংশন কাজ করে কিনা। + +![get-users](./images/get-users.png) + +আমরা দেখছি আমাদের সব ইউজার আমরা পেয়ে গেছি। + +কিন্তু আমরা যদি আমাদের রিকোয়ারমেন্ট অ্যানালাইসিসে গিয়ে রাউটগুলো দেখি তাহলে দেখবো সমস্ত ইউজার রিলেটেড রিকোয়েস্ট গুলো হবে প্রাইভেট। তাহলে আমরা আমাদের অথেনটিকেট মিডলওয়্যারকে প্রত্যেকটা রাউটের মধ্যে গিয়ে লিখে দিয়ে আসতে পারি। কিন্তু যেহেতু সব ইউজার রাউটই প্রাইভেট হবে, সুতরাং এত জায়গায় না লিখে আমরা আমাদের ইনডেক্স রাউটে গিয়ে ইউজারের প্রিফিক্সিং রাউট যেটা সেখানে লিখে দিলেই সব ইউজার রাউট প্রাইভেট হয়ে যাবে। + +```js +const router = require('express').Router(); +const authenticate = require('../middleware/authenticate'); +const authRoutes = require('./auth'); +const usersRoutes = require('./users'); + +router.use('/api/v1/auth', authRoutes); +router.use('/api/v1/users', authenticate, usersRoutes); + +module.exports = router; +``` + +এখন যদি আমরা রিকোয়েস্ট পাঠাই আমাদেরকে ম্যাসেজ দিবে Unauthorized। + +![unauth](./images/unauth.png) + +এখন যদি আমাদের ইউজার ইনফরমেশন দেখতে হয় তাহলে দরকার হবে টোকেন। আর টোকেন পেতে আমাদের লগইন করতে হবে। চলুন লগইন করে দেখি। + +![login-1](./images/login-1.png) + +আমরা টোকেন পেয়ে গেলাম। এবার টোকেন দিয়ে আমরা দেখি আমাদের ইউজার ইনফরমেশনগুলো পাই কিনা। + +![user-token](./images/user-token.png) + +এবার আমরা getUserById কন্ট্রোলার তৈরি করবো। এটার জন্য আমাদের দরকার একটা আইডি। এখন সেটা আসবে params থেকে। আমরা /users এর পর যা লিখবো সেগুলোই হচ্ছে params. আমাদের কন্ট্রোলারের জন্য আমরা আগেই সার্ভিস তৈরি করে রেখেছিলাম ইউজার সার্ভিসের মধ্যে। সেটা হলো findUserByProperty। এটা এখন আমরা আমাদের কন্ট্রোলারে ইউজ করবো। + +```js +// controller/users.js + +const getUserById = async (req, res, next) => { + const userId = req.params.userId; + + try { + const user = await userService.findUserByProperty('_id', userId); + + if (!user) { + throw error('User not found', 404); + } + // TODO: we have to delete the password from user object + return res.status(200).json(user); + } catch (e) { + next(e); + } +}; +``` + +এবার আমরা আমাদের রাউটারে এই কন্ট্রোলার ফাংশন পাস করে দিবো। + +```js +// routes/users.js + +/** + * Get user by id or email + */ + +router.get('/:userid', usersController.getUserById); +``` + +এবার চেক করার পালা। আমরা একটা আইডি নিয়ে আসি ডাটাবেজ থেকে। সেটা দিয়ে চেক করবো। যদি আইডি সঠিক থাকে তাহলে সে ঠিক আউটপুট দিবে। + +![getid](./images/getid.png) + +আর ভিন্ন কোনো আইডি হলে সে এরর দিবে। + +![getiderr](./images/getiderr.png) + +এবার আমরা postUser কন্ট্রোলার নিয়ে কাজ করবো। এটার জন্যও আমাদের সার্ভিস তৈরি করা আছে ইউজার সার্ভিসে। সেটা হলো createNewUser। এবার এটা ব্যবহার করে আমরা আমাদের কন্ট্রোলার তৈরি করে ফেলি। কিন্তু তার আগে আমাদের createNewUser ফাংশনে একটু চেইঞ্জ করতে হবে। আমরা যদি দেখি এই ফাংশনে এখন শুধু name, email and password দিয়ে অ্যাকাউন্ট তৈরি করতে পারছি। কিন্তু আমরা যদি roles বা accountStatus দিতে চাই তাহলে কিন্তু পারবো না। তাই আমরা এখন এখানে এই অপশনটা ক্রিয়েট করবো। ইউজার মডেলে আমরা দেখতে পাচ্ছি যে ডিফল্ট হিসেবে roles এ থাকে 'STUDENT' এবং accounStatus এ থাকবে 'PENDING'। + +```js +// service/user.js + +const createNewUser = ({ name, email, password, roles, accountStatus }) => { + const user = new User({ + name, + email, + password, + roles: roles ? roles : 'STUDENT', + accountStatus: accountStatus ? accountStatus : 'PENDING', + }); + return user.save(); +}; +``` + +```js +// controller/users.js + +const postUser = async (req, res, next) => { + const { name, email, password, roles, accountStatus } = req.body; + + try { + const user = await userService.createNewUser({ + name, + email, + password, + roles, + accountStatus, + }); + return res.status(201).json(user); + } catch (e) { + next(e); + } +}; +``` + +এখন এখানে পাসওয়ার্ড হ্যাশ করা হয়নি। হ্যাশ করার কাজটা করতে হবে আমাদের সার্ভিসে। কিন্তু আমাদের auth সার্ভিসের মধ্যে যে রেজিস্টার সার্ভিস আছে সেটাতে পাসওয়ার্ড হ্যাশ করা আছে। সুতরাং আমরা এখানে সেটা ইউজ করবো। তার আগে একটু কাজ করে নিতে হবে। + +```js +// service/auth.js + +const registerService = async ({ + name, + email, + password, + roles, + accountStatus, +}) => { + let user = await findUserByProperty('email', email); + if (user) { + throw error('User already exists', 400); + } + + const salt = await bcrypt.genSalt(10); + const hash = await bcrypt.hash(password, salt); + return createNewUser({ name, email, password: hash, roles, accountStatus }); +}; +``` + +```js +// controller/users.js + +const authService = require('../service/auth'); + +const postUser = async (req, res, next) => { + const { name, email, password, roles, accountStatus } = req.body; + + try { + const user = await authService.registerService({ + name, + email, + password, + roles, + accountStatus, + }); + return res.status(201).json(user); + } catch (e) { + next(e); + } +}; +``` + +যেহেতু রেজিস্টার সার্ভিসে কিছু পরিবর্তন করেছি সুতরাং একটু চেক করে দেখি যে এটা ঠিকভাবে কাজ করছে কিনা। + +![reg](./images/reg.png) + +ঠিকভাবেই কাজ করছে। + +এবার ইউজার রাউটসে গিয়ে আমরা এই কন্ট্রোলার পাস করে দিবো। + +```js +// routes/users.js + +/** + * Create new user + */ + +router.post('/', usersController.postUser); +``` + +এবার আমরা একটা নতুন ইউজার তৈরি করে দেখি। + +![new-user](./images/new-user.png) + +একটা জিনিস মাথায় রাখতে হবে। যদি টোকেন না দেন এটা কাজ করবে না, যেহেতু এটা প্রাইভেট রাউট। + +এবার আমরা ডিলিটের জন্য কন্ট্রোলার তৈরি করবো। + +```js +// controller/users.js + +const deleteUserById = async (req, res, next) => { + const { userId } = req.params; + + try { + const user = await userService.findUserByProperty('_id', userId); + + if (!user) { + throw error('User not found', 404); + } + + await user.remove(); + return res.status(203).send(); + } catch (e) { + next(e); + } +}; +``` + +```js +// routes/users.js + +/** + * Delete user by id + */ + +router.delete('/:userId', usersController.deleteUserById); +``` + +![del](./images/del.png) + +আমাদের ডিলিটের কাজ শেষ। এবার বাকি আছে শুধু আপডেটের কাজ। তার আবার দুইটা মেথড put and patch। প্রথমে patch এর কাজ করি। আমরা কি কি আপডেট করতে দিবো আগে সিদ্ধান্ত নিই। আমরা ইমেইল আর পাসওয়ার্ড আপডেট করতে দিবো না patch মেথডে। বাকি সব আপডেট করতে দিবো। + +```js +// controller/users.js + +const patchUserById = async (req, res, next) => { + const { userId } = req.params; + + const { name, roles, accountStatus } = req.body; + + try { + const user = userService.findUserByProperty('_id', userId); + + if (!user) { + throw error('User not found', 404); + } + + user.name = name ?? user.name; + user.roles = roles ?? user.roles; + user.accountStatus = accountStatus ?? user.accountStatus; + + await user.save(); + return res.status(200).json(user); + } catch (e) { + next(e); + } +}; +``` + +```js +// routes/users.js + +/** + * update user by id + * @method PATCH + */ + +router.patch('/:userId', usersController.patchUserById); +``` + +![patch](./images/patch.png) + +এবার আমরা put এর জন্য কাজ করবো। প্রথমে আমাদের ইউজার সার্ভিসে আপডেট করার একটা ফাংশন বানাতে হবে। + +```js +// service.user.js + +const updateUser = async (id, data) => { + const user = await findUserByProperty('email', email); + + if (user) { + throw error('Email already in use', 400); + } + + return User.findByIdAndUpdate(id, { ...data }, { new: true }); +}; + +module.exports = { + updateUser, +}; +``` + +```js +// controller/users.js + +const putUserById = async (req, res, next) => { + const { userId } = req.params; + const { name, email, roles, accountStatus } = req.body; + + try { + const user = await userService.updateUser(userId, { + name, + email, + roles, + accountStatus, + }); + + if (!user) { + throw error('User not found', 404); + } + + return res.status(200).json(user); + } catch (e) { + next(e); + } +}; +``` + +```js +// routes/users.js + +/** + * update user by id + * @method PUT + */ + +router.put('/:userId', usersController.putUserById); +``` + +![put](./images/put.png) + +সব ঠিকভাবে কাজ করছে। আমাদের user crud ম্যানেজ করার কাজ শেষ। কিছু টাস্ক আছে যা আমরা টুডুতে রেখে দিয়েছি। সেগুলো আমরা পরে করবো। + +## Source Code + +এই লেকচারের সোর্স কোডসমূহ এই [লিংক](../../src/lecture-24/) এ পাবেন। + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) \ No newline at end of file diff --git a/full-stack-army/class-overview/Lecture-24/images/del.png b/full-stack-army/class-overview/Lecture-24/images/del.png new file mode 100644 index 0000000..bc17528 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-24/images/del.png differ diff --git a/full-stack-army/class-overview/Lecture-24/images/get-users.png b/full-stack-army/class-overview/Lecture-24/images/get-users.png new file mode 100644 index 0000000..ef58487 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-24/images/get-users.png differ diff --git a/full-stack-army/class-overview/Lecture-24/images/getid.png b/full-stack-army/class-overview/Lecture-24/images/getid.png new file mode 100644 index 0000000..9e2275f Binary files /dev/null and b/full-stack-army/class-overview/Lecture-24/images/getid.png differ diff --git a/full-stack-army/class-overview/Lecture-24/images/getiderr.png b/full-stack-army/class-overview/Lecture-24/images/getiderr.png new file mode 100644 index 0000000..3a9f78f Binary files /dev/null and b/full-stack-army/class-overview/Lecture-24/images/getiderr.png differ diff --git a/full-stack-army/class-overview/Lecture-24/images/login-1.png b/full-stack-army/class-overview/Lecture-24/images/login-1.png new file mode 100644 index 0000000..414826f Binary files /dev/null and b/full-stack-army/class-overview/Lecture-24/images/login-1.png differ diff --git a/full-stack-army/class-overview/Lecture-24/images/new-user.png b/full-stack-army/class-overview/Lecture-24/images/new-user.png new file mode 100644 index 0000000..514aea4 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-24/images/new-user.png differ diff --git a/full-stack-army/class-overview/Lecture-24/images/patch.png b/full-stack-army/class-overview/Lecture-24/images/patch.png new file mode 100644 index 0000000..3352be2 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-24/images/patch.png differ diff --git a/full-stack-army/class-overview/Lecture-24/images/put.png b/full-stack-army/class-overview/Lecture-24/images/put.png new file mode 100644 index 0000000..819a188 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-24/images/put.png differ diff --git a/full-stack-army/class-overview/Lecture-24/images/reg.png b/full-stack-army/class-overview/Lecture-24/images/reg.png new file mode 100644 index 0000000..cce39ec Binary files /dev/null and b/full-stack-army/class-overview/Lecture-24/images/reg.png differ diff --git a/full-stack-army/class-overview/Lecture-24/images/unauth.png b/full-stack-army/class-overview/Lecture-24/images/unauth.png new file mode 100644 index 0000000..315f2ae Binary files /dev/null and b/full-stack-army/class-overview/Lecture-24/images/unauth.png differ diff --git a/full-stack-army/class-overview/Lecture-24/images/user-crud.png b/full-stack-army/class-overview/Lecture-24/images/user-crud.png new file mode 100644 index 0000000..a1c5cc0 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-24/images/user-crud.png differ diff --git a/full-stack-army/class-overview/Lecture-24/images/user-token.png b/full-stack-army/class-overview/Lecture-24/images/user-token.png new file mode 100644 index 0000000..8b01289 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-24/images/user-token.png differ diff --git a/full-stack-army/class-overview/Lecture-25/README.md b/full-stack-army/class-overview/Lecture-25/README.md new file mode 100644 index 0000000..321bdcd --- /dev/null +++ b/full-stack-army/class-overview/Lecture-25/README.md @@ -0,0 +1,7 @@ +# Lecture 25 - QNA on 5 Recorded Courses and Motivational ADDA + +এখানে আজ কিছু রেকর্ডেড ভিডিওর উপর আলোচনা এবং আড্ডা দেয়া হয়েছে। এখানে আসলে লেখার মতো কিছু নাই। এই QNA সেকশনগুলো আমি লিখবো না। এটা আপনারা শুনবেন। নিজের মতো করে যা যা দরকার সব নিয়ে নিবেন। ভিডিওটা ভাল করে দেখলেই আশা করি হয়ে যাবে। + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/full-stack-army/class-overview/Lecture-26/README.md b/full-stack-army/class-overview/Lecture-26/README.md new file mode 100644 index 0000000..68b2c70 --- /dev/null +++ b/full-stack-army/class-overview/Lecture-26/README.md @@ -0,0 +1,486 @@ +# Lecture 26 - Implement Attendance System Main Functionalities + +গতো ক্লাসে আমরা user crud নিয়ে কাজ করেছিলাম। যদিও কিছু কাজ আমরা বাকি রেখেছিলাম। সেগুলো আমরা পরে করবো। আমাদের এই ক্লাসের কাজ হচ্ছে Timesheer and Attendance. + +![notion](./images/notion.png) + +আমাদের টার্গেট হচ্ছে একটা বাটন আছে। আমি অ্যাডমিন। আমি যদি বাটনে ক্লিক করি তাহল অ্যাটেনডেন্স এনেবল হয়ে যাবে। আমরা চাইলে একাধিকবার এটা এনেবল করতে পারবো। তবে খেয়াল রাখতে হবে যদি একই টাইমে একট অ্যাটেনডেন্স এনেবল থাকে তাহলে আর এনেবল করা যাবে না। একই সময় একট অ্যাটেনডেন্স এনেবল থাকবে। এটা এনেবল করার অর্থ হলো একটা ডাটা ক্রিয়েট করে ডাটাবেজে রাখা, যে এই সময়ে একটা অ্যাটেনডেন্স এনেবল হয়েছে এবং সেটা করেছে অ্যাডমিন। এবং এটা কতক্ষণের জন্য এনেবল থাকবে সেটা আমরা টাইম দিয়ে পরবর্তীতে ক্যালকুলেট করবো। + +আমরা যদি আমাদের অ্যাটেনডেন্স মডেলের দিকে তাকাই তাহলে দেখবো আমাদের মডেল তৈরি করা আছে এভাবে। + +```js +// models/AdminAttendance.js + +const { Schema, model } = require('mongoose'); + +const adminAttendanceSchema = new Schema({ + timeLimit: Number, + status: String, + createdAt: Date, +}); + +const AdminAttendance = model('AdminAttendance', adminAttendanceSchema); + +module.exports = AdminAttendance; +``` + +এখানে createdAt এর সাথে updatedAt ও আমরা রাখতে পারি, কোন সময় আপডেট করা হয়েছে সেটা জানার জন্য। আমরা createdAt এর পুরো লাইনটা বাদ দিয়ে দেবো। createdAt ও updatedAt এর কাজ আমরা করতে পারি timestamps এর মাধ্যমে। যখন কোনো ডাটা ক্রিয়েট হয় তখন অটোমেটিক্যালি createdAt এবং updatedAt timestamps এর মধ্যে তৈরি হয়। আমরা যখন ডাটা আপডেট করবো সেই টাইমটা updatedAt এর মধ্যে স্টোর হয়ে যাবে। সুতরাং আমরা এই ফাইলটাকে একটু চেইঞ্জ করতে পারি নিচের মতো করে। + +```js +// models/AdminAttendance.js + +const { Schema, model } = require('mongoose'); + +const adminAttendanceSchema = new Schema( + { + timeLimit: { + type: Number, + required: true, + max: 30, + min: 5, + default: 5, + }, + status: { + type: String, + required: true, + enum: ['RUNNING', 'COMPLETED'], + default: 'RUNNING', + }, + }, + { timestamps: true } +); + +const AdminAttendance = model('AdminAttendance', adminAttendanceSchema); + +module.exports = AdminAttendance; +``` + +এবার আমাদের এই মডেলটা ব্যবহার করে রাউট আর কন্ট্রোলার তৈরি করতে হবে। আমরা জানি প্রথমে যদি আমরা রাউট তৈরি করতে পারি তাহলে আমাদের জন্য কাজ সহজ হয়ে যাবে। চলুন রাউট তৈরি করে ফেলি। + +আমরা routes ফোল্ডারের মধ্যে admin-attendance.js নামে একটা ফাইল তৈরি করবো। এবং আমাদের এনেবল আর ডিজেবলের জন্য রাউট তৈরি করবো। + +```js +// routes/admin-attendance.js + +const router = require('express').Router(); + +router.get('/enable', () => {}); +router.get('/disable', () => {}); + +module.exports = router; +``` + +এবার আমাদের কাজ কন্ট্রোলার তৈরি করা। আমরা কন্ট্রোলারের কাছে গিয়ে admin-attendance.js নামে একটা ফাইল ক্রিয়েট করলাম। + +```js +// controller/admin-attendance.js + +const getEnable = (req, res, next) => {}; + +const getDisable = (req, res, next) => {}; + +module.exports = { + getEnable, + getDisable, +}; +``` + +এবার এই ফাংশনগুলোকে আমরা রাউটের মধ্যে পাস করে দিবো। + +```js +// routes/admin-attendance.js + +const { getEnable, getDisable } = require('../controller/admin-attendance'); + +const router = require('express').Router(); + +router.get('/enable', getEnable); +router.get('/disable', getDisable); + +module.exports = router; +``` + +এবার আমরা আমাদের রাউটকে মেইন রাউটের সাথে অর্থাৎ index.js এর সাথে কানেক্ট করবো। + +```js +// routes/index.js + +const router = require('express').Router(); +const authenticate = require('../middleware/authenticate'); +const authRoutes = require('./auth'); +const usersRoutes = require('./users'); +const adminAttendanceRoute = require('./admin-attendance'); + +router.use('/api/v1/auth', authRoutes); +router.use('/api/v1/users', authenticate, usersRoutes); +router.use('/api/v1/admin/attendance', authenticate, adminAttendanceRoute); + +module.exports = router; +``` + +যেহেতু এটা একটা প্রাইভেট রাউট তাই আমরা আমাদের authenticate মিডলওয়্যারটা যুক্ত করে দিলাম। + +এবার আমরা আমদের getEnable কন্ট্রোলার ফাংশনটা কমপ্লিট করি। + +```js +// controller/admin-attendance.js + +const AdminAttendance = require('../models/AdminAttendance'); + +const getEnable = async (req, res, next) => { + try { + const attendance = new AdminAttendance(); + await attendance.save(); + return res.status(201).json({ message: 'Success', attendance }); + } catch (e) { + next(e); + } +}; +``` + +আমরা আলাদাভাবে সার্ভিস না বানিয়ে সার্ভিসের কাজটা আপাতত এখানেই করছি বুঝার সুবিধার্থে। এবার এই ফাংশনটা কাজ করছে কিনা একটু চেক করবো। তবে তার আগে যেহেতু এটা প্রাইভেট রাউট সেহেতু লগইন করে আমাদের টোকেন নিয়ে নিলাম। + +![get-enable](./images/get-enable.png) + +একটা অবজেক্ট এসেছে। যেখানে আমাদের মডেলে যেমন যেমন যা চেয়েছিলাম সেগুলো আছে। + +এখন আবার যদি আমরা এই রাউটে হিট করি তাহলে আরেকটা টাইমশীট তৈরি হবে। যেটা আমরা শুরুতে বলেছিলাম আমরা চাই না। সেক্ষেত্রে আমাদের কন্ট্রোলারের আমাদের এটা নিয়ে একটু কাজ করতে হবে। যদি স্ট্যাটাস রানিং থাকে তাহলে সে অতটুকুতেই থেমে যাবে আর আমাকে ওয়ার্নিং দিবে। আর নিচে আসতে পারবে না। + +```js +// controller/admin-attendance.js + +const AdminAttendance = require('../models/AdminAttendance'); +const error = require('../utils/error'); + +const getEnable = async (req, res, next) => { + try { + const running = await AdminAttendance.findOne({ status: 'RUNNING' }); + + if (running) { + throw error('Already Running', 400); + } + const attendance = new AdminAttendance(); + await attendance.save(); + return res.status(201).json({ message: 'Success', attendance }); + } catch (e) { + next(e); + } +}; +``` + +![running](./images/running.png) + +এখানে দেখুন আমরা যখন রানিং থাকা অবস্থায় আবার হিট করলাম আমাদের দেখাচ্ছে Already Running. + +এবার আমরা করবো getDisable এর কাজ। এটা করার আগে আমাদের স্ট্যাটাস জানার ব্যাপার আছে। তাই আমরা প্রথমে একটা রাউট ক্রিয়েট করে নিবো। এবং তার জন্য কন্ট্রোলার ফাংশন তৈরি করবো। + +```js +// routes/admin-attendance.js + +const { + getEnable, + getDisable, + getStatus, +} = require('../controller/admin-attendance'); + +const router = require('express').Router(); + +router.get('/enable', getEnable); +router.get('/disable', getDisable); +router.get('/status', getStatus); + +module.exports = router; +``` + +```js +// controller/admin-attendance.js + +const getStatus = async (req, res, next) => { + try { + const running = await AdminAttendance.findOne({ status: 'RUNNING' }); + + if (!running) { + throw error('Not Running', 400); + } + + return res.status(200).json(running); + } catch (e) { + next(e); + } +}; + +module.exports = { + getStatus, +}; +``` + +এবার আমরা যদি রিকোয়েস্ট পাঠাই status রাউটে তাহলে দেখবো সেটা আমাদের দেখাচ্ছে status running। + +![status](./images/status.png) + +আমাদের সিস্টেমের ডিজাইন হচ্ছে যদি টাইমশীট এনেবল থাকে তবে নির্দিষ্ট সময় পর সেটা ডিজেবল হয়ে যাবে। এখানে যদি রানিং না থাকে সেই কোড লিখেছি। যদি রানিং থাকে তাহলে প্রথমে টাইম চেক করতে হবে যে টাইম আছে কিনা। সেটা চেক করার জন্য আমরা একটা লাইব্রেরি ইনস্টল করবো। সেটা হচ্ছে [date-fns](https://date-fns.org/)। + +এখন আমাদের চেক করতে হবে createdAt টাইমটা, কারেন্ট টাইম আর টাইম লিমিট এর যোগফল থেকে বড় কিনা। যদি বড় হয় তাহলে সেটা এক্সপায়ার হয়ে গেছে। এই যোগ করার জন্য date-fns এ addMinutes নামের একটা ফাংশন আছে। আমরা এর মধ্যে আর্গুমেন্ট আকারে প্রথমে createdAt টাইম এবং পরে আমাদের টাইমলিমিট দিয়ে দিবো। যেহেতু ডাটাবেজে টাইম স্ট্রিং আকারে আছে সেহেতু ওখানে থেকে Date অবজেক্টের মাধ্যমে আমাদের টাইমটা কনভার্ট করে নিতে হবে। টাইম কমপেয়ার করার জন্য date-fns এ isAfter নামক একটা ফাংশন আছে। চলুন কাজগুলো করে নিই। + +```js +// controller/admin-attendance.js + +const getStatus = async (req, res, next) => { + try { + const running = await AdminAttendance.findOne({ status: 'RUNNING' }); + + if (!running) { + throw error('Not Running', 400); + } + + const started = addMinutes(new Date(running.createdAt), running.timeLimit); + + if (isAfter(new Date(), started)) { + running.status = 'COMPLETED'; + await running.save(); + } + + return res.status(200).json(running); + } catch (e) { + next(e); + } +}; +``` + +এবার যদি আমরা রিকোয়েস্ট পাঠাই দেখবো স্ট্যাটাস কমপ্লিট হয়ে গেছে। + +![status-com](./images/status-com.png) + +এবার আমরা getDisable নিয়ে কাজ করবো। + +```js +// controller/admin-attendance.js + +const getDisable = async (req, res, next) => { + try { + const running = await AdminAttendance.findOne({ status: 'RUNNING' }); + + if (!running) { + throw error('Not Running', 400); + } + + running.status = 'COMPLETED'; + await running.save(); + + return res.status(200).json(running); + } catch (e) { + next(e); + } +}; +``` + +এবার আমাদের রিকোয়েস্ট চেক করি। দেখা যাচ্ছে ডিজেবল রাউটে হিট করলে রানিং টাইমশীট কমপ্লিটেড হয়ে যাচ্ছে। + +![disable](./images/disable.png) + +এখন এনেবল করার পর যে টাইমশীট ক্রিয়েট করবে সেটার আইডির অ্যাগেইনস্টে ছাত্ররা অ্যাটেনডেন্স দিতে পারবে। এবার আমরা কাজ করবো শেষের দুইটা রাউট নিয়ে। টাইমশীট নিয়ে আমরা পরে কাজ করবো। + +![notion](./images/notion-2.png) + +এই দুইটা রাউট কমপ্লিট হলে আমরা ফ্রন্টএন্ডে মুভ করতে পারবো। ব্যাকএন্ডে আমরা অনেক কাজ ফেলে যাবো। সেগুলো আমরা পরে করবো। + +আমরা প্রথমে student-attendance রাউট বানিয়ে ফেলি। + +```js +// routes/student-attendance.js + +const router = require('express').Router(); + +router.get('/status', () => {}); +router.get('/:id', () => {}); + +module.exports = router; +``` + +আমরা এই রাউটকে index.js এ কানেক্ট করবো। + +```js +// routes/index.js + +const router = require('express').Router(); +const authenticate = require('../middleware/authenticate'); +const authRoutes = require('./auth'); +const usersRoutes = require('./users'); +const adminAttendanceRoutes = require('./admin-attendance'); +const studentAttendanceRoutes = require('./student-attendance'); + +router.use('/api/v1/auth', authRoutes); +router.use('/api/v1/users', authenticate, usersRoutes); +router.use('/api/v1/admin/attendance', authenticate, adminAttendanceRoutes); +router.use('/api/v1/student/attendance', authenticate, studentAttendanceRoutes); + +module.exports = router; +``` + +এবার আমরা StudentAttendance মডেলে পূর্বের মতো createdAt কেটে দিয়ে timestamps ইউজ করবো। + +```js +// models/StudentAttendance.js + +const { Schema, model } = require('mongoose'); + +const studentAttendanceSchema = new Schema( + { + user: { + type: Schema.Types.ObjectId, + ref: 'User', + required: true, + }, + adminAttendance: { + type: Schema.Types.ObjectId, + ref: 'AdminAttendance', + required: true, + }, + }, + { timestamps: true } +); + +const StudentAttendance = model('StudentAttendance', studentAttendanceSchema); + +module.exports = StudentAttendance; +``` + +এবার তৈরি করবো আমাদের কন্ট্রোলার। + +```js +// controller/student-attendance.js + +const StudentAttendance = require('../models/StudentAttendance'); + +const getAttendance = (req, res, next) => {}; + +const getAttendanceStatus = (req, res, next) => {}; + +module.exports = { + getAttendance, + getAttendanceStatus, +}; +``` + +```js +// routes/student-attendance.js + +const { + getAttendance, + getAttendanceStatus, +} = require('../controller/student-attendance'); + +const router = require('express').Router(); + +router.get('/status', getAttendanceStatus); +router.get('/:id', getAttendance); + +module.exports = router; +``` + +আমরা এবার getAttendance এবং getAttendanceStatus কন্ট্রোলারগুলো কমপ্লিট করি। + +```js +// controller/student-attendance + +const { addMinutes, isAfter } = require('date-fns'); +const AdminAttendance = require('../models/AdminAttendance'); +const StudentAttendance = require('../models/StudentAttendance'); +const error = require('../utils/error'); + +const getAttendance = async (req, res, next) => { + const { id } = req.params; + + try { + /** + * Step 1 - Find admin attendance by id + * Step 2 - Check if it is running or not + * Step 3 - Check already register or not + * Step 4 - Register entry + */ + + const adminAttendance = await AdminAttendance.findById(id); + + if (!adminAttendance) { + throw error('Invalid Attendance ID', 400); + } + + if (adminAttendance.status === 'COMPLETED') { + throw error('Attendance already completed'); + } + + let attendance = await StudentAttendance.findOne({ + adminAttendance: id, + user: req.user._id, + }); + + if (attendance) { + throw error('Already registered', 400); + } + + attendance = new StudentAttendance({ + user: req.user._id, + adminAttendance: id, + }); + + await attendance.save(); + return res.status(201).json(attendance); + } catch (e) { + next(e); + } +}; + +const getAttendanceStatus = async (req, res, next) => { + try { + const running = await AdminAttendance.findOne({ status: 'RUNNING' }); + + if (!running) { + throw error('Not Running', 400); + } + + const started = addMinutes(new Date(running.createdAt), running.timeLimit); + + if (isAfter(new Date(), started)) { + running.status = 'COMPLETED'; + await running.save(); + } + + return res.status(200).json(running); + } catch (e) { + next(e); + } +}; + +module.exports = { + getAttendance, + getAttendanceStatus, +}; +``` + +আমরা একটা অ্যাটেনডেন্স এনেবল করে প্রথমে স্ট্যাটাস দেখার চেষ্টা করি। + +![st-status](./images/st-status.png) + +দেখা যাচ্ছে রানিং আছে। এবার আমরা অ্যাটেনডেন্স দেয়ার চেষ্টা করি। + +![attend](./images/attend.png) + +অ্যাটেনডেন্স ক্রিয়েট হয়েছে। এখন যদি আবার এই ইউজার থেকে আমরা অ্যাটেনডেন্স দিতে চাই তা আর নিবে না। + +![attend-fail](./images/attend-fail.png) + +এবার ভিন্ন একটা আইডিতে লগইন করে সেটার টোকেন দিয়ে অ্যাটেনডেন্স দেয়ার চেষ্টা করি। + +![attend](./images/attend-2.png) + +সাক্সেসফুলি ডান। যতজন লগইন করবে ততজনই অ্যাটেনডেন্স দিতে পারবে। কিন্তু একবারের বেশি এক ইউজার অ্যাটেন্ডেন্স দিতে পারবে না। + +আমাদের সিস্টেম মোটামুটি কমপ্লিট। এবার আমরা ফ্রন্টএন্ডে মুভ করবো। ব্যাকএন্ডে এখনও অনেক কাজ আছে। সেগুলো আমরা যখন লাগবে সেটা বুঝে এগোবো। এতটুকু রেডি আছে মানে আমরা এখন ফ্রন্টএন্ডে জাম্প করতে পারি। + +## Source Code + +এই লেকচারের সোর্স কোডসমূহ এই [লিংক](../../src/lecture-26/) এ পাবেন। + +## AUTHOR + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/full-stack-army/class-overview/Lecture-26/images/attend-2.png b/full-stack-army/class-overview/Lecture-26/images/attend-2.png new file mode 100644 index 0000000..5bac56d Binary files /dev/null and b/full-stack-army/class-overview/Lecture-26/images/attend-2.png differ diff --git a/full-stack-army/class-overview/Lecture-26/images/attend-fail.png b/full-stack-army/class-overview/Lecture-26/images/attend-fail.png new file mode 100644 index 0000000..6c4782b Binary files /dev/null and b/full-stack-army/class-overview/Lecture-26/images/attend-fail.png differ diff --git a/full-stack-army/class-overview/Lecture-26/images/attend.png b/full-stack-army/class-overview/Lecture-26/images/attend.png new file mode 100644 index 0000000..6d32d28 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-26/images/attend.png differ diff --git a/full-stack-army/class-overview/Lecture-26/images/disable.png b/full-stack-army/class-overview/Lecture-26/images/disable.png new file mode 100644 index 0000000..2036944 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-26/images/disable.png differ diff --git a/full-stack-army/class-overview/Lecture-26/images/get-enable.png b/full-stack-army/class-overview/Lecture-26/images/get-enable.png new file mode 100644 index 0000000..887203d Binary files /dev/null and b/full-stack-army/class-overview/Lecture-26/images/get-enable.png differ diff --git a/full-stack-army/class-overview/Lecture-26/images/notion-2.png b/full-stack-army/class-overview/Lecture-26/images/notion-2.png new file mode 100644 index 0000000..216dffa Binary files /dev/null and b/full-stack-army/class-overview/Lecture-26/images/notion-2.png differ diff --git a/full-stack-army/class-overview/Lecture-26/images/notion.png b/full-stack-army/class-overview/Lecture-26/images/notion.png new file mode 100644 index 0000000..a5fe8b8 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-26/images/notion.png differ diff --git a/full-stack-army/class-overview/Lecture-26/images/running-1.png b/full-stack-army/class-overview/Lecture-26/images/running-1.png new file mode 100644 index 0000000..baf002b Binary files /dev/null and b/full-stack-army/class-overview/Lecture-26/images/running-1.png differ diff --git a/full-stack-army/class-overview/Lecture-26/images/running.png b/full-stack-army/class-overview/Lecture-26/images/running.png new file mode 100644 index 0000000..01e8f54 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-26/images/running.png differ diff --git a/full-stack-army/class-overview/Lecture-26/images/st-status.png b/full-stack-army/class-overview/Lecture-26/images/st-status.png new file mode 100644 index 0000000..254241e Binary files /dev/null and b/full-stack-army/class-overview/Lecture-26/images/st-status.png differ diff --git a/full-stack-army/class-overview/Lecture-26/images/status-com.png b/full-stack-army/class-overview/Lecture-26/images/status-com.png new file mode 100644 index 0000000..8ed12bf Binary files /dev/null and b/full-stack-army/class-overview/Lecture-26/images/status-com.png differ diff --git a/full-stack-army/class-overview/Lecture-26/images/status.png b/full-stack-army/class-overview/Lecture-26/images/status.png new file mode 100644 index 0000000..11e07fe Binary files /dev/null and b/full-stack-army/class-overview/Lecture-26/images/status.png differ diff --git a/full-stack-army/class-overview/Lecture-27/Client Server.drawio b/full-stack-army/class-overview/Lecture-27/Client Server.drawio new file mode 100644 index 0000000..0b730fc --- /dev/null +++ b/full-stack-army/class-overview/Lecture-27/Client Server.drawio @@ -0,0 +1 @@ +7V3bcqM4EP0aPyaFEDc/xrfJTnm2UpNU7WTfNEbGbDDyghzH+/UrjMAggU18g2DPS4wuIOt0n251t5kO7M8/vgVoMftBbOx1VMX+6MBBR1WBppnsT9Syjlu6lhU3OIFr80Hbhmf3P8wbFd66dG0c5gZSQjzqLvKNE+L7eEJzbSgIyCo/bEq8/FMXyMFSw/MEeXLrX65NZ3GrZliZjkfsOjP+aAgVvvLfaPLmBGTp8wd2VKha0AD8+89RcjM+Ppwhm6wyTXDYgf2AEBp/mn/0sRdtbrJv8bxRSW+6vgD7tMoE9fsY3f3h+vDt79cXRF4X2Hm843d5R94SJ1/D8Nj9elPCbstWTdd8q4x/lyTpuAs3QD6wAaq2YMLQ2/azT070dxSwgZjtDr8hW1l8z7ibb0p6e3U1cyl+XqBJdL1i0sYGzejcY1eAfUThIsZ/6n5gO1qH63l94pFgMx32+kZf1Vl7SAPyhjM9g8ED0Id85Zn20XD0MFTSpbzjgOKP0s0FKWRMFzCZYxqs2RA+QYUc5XUi3vx6tRUqoPG2WUae0kbEBdlJ773Fkn3gcH4GWkXaYmwz2eeXJKAz4hAfecNta28jz9H2DqKN2Y4ZE7LgQPyDKV1zRUZLSvIwRXvMO1WNXXvoN/Z6qapktp8ry+GARd9mN1zsy5NlMME7dkkthjXAHqLue/7+RRDxqU/E3agLFwdoCeJgCChTFDiY8lkC0OkyDseeKcLnsZ8sg/cU+q0gRKiGb5hOZodLBYMlWP/i8zcXr9HFvZ5cDj6ynYM1vxKlqUwgQraf9CEyB6xj4qEwdCdJ88j1tsuw5UGsMTNkhzCW8M2phZEZwI1w7Bp3rNQeRSyq0SzhuglJ8ThYq5CYzRKSMgYybxR0mHRptUrX+dzWyFe5Fq8VKHreT+nW77W2yHMpponPKndFYjkhB8CKHABqdUOgxAE/8SLAIfu67OnEZ31jtMaBJE95AdmjwBL9N0l/VS2vvhDI+qsaBforHkdOBoomgTJAFF0BFKbSNChAA9300zBiJR/q0qR5NBkWRxIsqNwrmX9qXsqsy8YVkuBm82WqPmd86zFsnYTXnI9Q7DGcXBarWHDzLEJrgDwZ6vDCYiqfD8bEcSfIuwJLpAmxR02t3RJ1m8UaGQ01shoK7hWg7dHSzdUTDly2NTjo7HP2WxETAGZFOqk3LCnnsh5fXp4KRW8cZSWi47nnOn607WxPIjR7keZFPPHAO+aubceJERy6/6Hfm5tE0C4i7tp8Bb3X0Qc7hKAkA+ITH6e9JLBxIPTs4JtSluBZUr7OTpp7zKK+Q0VLOeWO2TKogByvJEGYQ21Fehs1P4VMpyE+j1kwJQn5E9MVCd6u0CzoWt1mIQG+IIoXLpB/VBTvZeV6LskE8eI7tjKIB4WjZwGwaTo6F8RTzoasHJs5GbLPzDB+i2pLrhJbtcCZuzS4coznZOB+I8RhU1XlB8PpKgFOT/T1AQyBtMdNcdc/daA+KHPHna+si658ARddtaq66CWO3oVi9mqzROsmIkUgKXWKSLLMjHnpk/mC7TmPFbXTPTcEO2DqdbvnWmMzsTczsJvdq+h4rcVBWgPTCI0WkQbkFKqL1rGVQSWJMCGnACQHuCSpwNBA68wwHrkrfZDWFR6kQKGwXpjQ1XdPYB/iNZw0mJXgkbGTzxRRPF16LTaT0Mjvdbf2NDtsGJdlqUJTc2RxrwDzqxvLwvzLCWkuyVSen+aOs6ANK+5IpQ7kJe4s1bVf1DmrLlrHOmcH2b2uIZoxsNvumcmvTA6eAMAFDGWy7aKh9HAYtthSWrBpljLJSzSOs24Hyj1UVIWz6o0rygmnfrSdrOkagkeiVwyUgizChZW9uTU/N2Xf6dJWUHZNrVXZ5QTkaOlPopr/TYHfNWi8pYoab9at8XqDD8I3jd9ltStovF5rTgjKsa6xO8WT9SQqFmitkktmvaj648JmXS6yzNCt8oQoxYEvn6s+W6SRfw/FoSWUOQqprtcC1KPRoGsYexXyuDoQs4oHV1QHop6tDkS/uXBfjtBjz6zxhJ4sM1urTchbq+MxosfWADKXHen+MqRkztqK4bjR+L4f1Isv/in0y0ERj4NzwWyoNx7/ajyepJT38rhRIpCX4fFkmbla4FIKV/ZTePspAop1DFpBYN4qfOnG2RhCl736MYl/LblJlXSigmwfOXje7qgKFG20Buu20brsKWUPXC8BbvMJWBdrceoHxJAp7xoCjs2rTzVvP1P4cm6Nkbzid69bc5Iiws+WQJjiW1yTt7qUVTRAsaJBmHCeigazuQ69BnOyf6fcK6a1RwFKf1J/0wpxoF6ns2/CxordjXCPFi2rVtHSbqLVWtEyjw01X8aWAwDrMOaGfPb+/vzrik4SQC946+iFz3Ry+VgS9xi7U+r6TovxsMTUYwPO2AWJCeLbblri85PxIHMYW40L6Ip0VPuR29AlXMZuSK8EEM1oHiDyj6VHJJi3OY9qSla6dhRMOSQ4fI/js4/It712qwVQlAsiwi63/2VS7INt/2MqOPwf \ No newline at end of file diff --git a/full-stack-army/class-overview/Lecture-27/README.md b/full-stack-army/class-overview/Lecture-27/README.md new file mode 100644 index 0000000..b698b48 --- /dev/null +++ b/full-stack-army/class-overview/Lecture-27/README.md @@ -0,0 +1,135 @@ +# Lecture 27 - Frontend Core Concepts and Communication + +## Table of contents + +- [What is Frontend](#what-is-frontend) +- [Features of Frontend](#features-of-frontend) +- [Why Frameworks](#why-frameworks) +- [Core Features of ReactJS](#core-features-of-reactjs) + +## What is Frontend + +গত ক্লাস পর্যন্ত আমরা আমাদের অ্যাপ্লিকেশনের ব্যাকএন্ড বা মন প্রাণ তৈরি করেছি। এই ক্লাস থেকে আমরা আমাদের অ্যাপ্লিকেশনের শারীরিক বা বাহ্যিক রূপ কিভাবে দেয়া যায় সেটা দেখবো। এই ক্লাসে ফ্রন্টএন্ড সম্পর্কে একটা বৃহৎ ধারণা দেয়া হবে। যে ধারণাটা পেলে আশা করি আপনাদের ডেস্কটপ, ওয়েব, মোবাইল কোনো প্ল্যাটফর্মের ডেভেলপমেন্ট নিয়ে ভাবতে হবে না। সব একই। + +আমরা এতদিন পর্যন্ত যা বানিয়েছি সেটা হলো API। এই API নিয়েও অনেকে বিজনেস করছে। উদাহরণস্বরূপ বলা যায় [Twilio](https://www.twilio.com/) এর কথা। এরা SMS, Voice, Video এবং Authentication এর API দিয়ে বিজনেস করছে। + +কিন্তু আমরা ফুলস্ট্যাক ডেভেলপার হতে চাইছি। তাই আমাদের শুধু API নিয়ে সন্তুষ্ট থাকলে চলবে না। আমাদের ফ্রন্টএন্ড নিয়েও ভাল ধারণা থাকতে হবে। + +আমাদের প্রথমে ক্লায়েন্টের সংজ্ঞা বুঝতে হবে। ক্লায়েন্ট অনেক ধরণের হতে পারে। আমাদের বুঝতে হবে কে কে আমাদের ক্লায়েন্ট। ডেভেলপমেন্ট জগতে ক্লায়েন্ট হলো মূলত ব্রাউজার, ডেস্কটপ, মোবাইল এসব। যারা আমাদের API ব্যবহার করবে। আমাদের ক্লায়েন্ট বলতে মনে আসে যারা আমাদের কাজ দিবে বা যারা ইউজার তারা ক্লায়েন্ট। কিন্তু খেয়াল করলে দেখা যাবে ইউজার আমাদের এপিআই কিন্তু ব্যবহার করবে না। ধরেন আপনি ইউজার। আপনি ফেসবুকে ঢুকলেন। ঢুকে যদি ইনস্পেক্টে গিয়ে নেটওয়ার্ক ট্যাবে যান এবং সেখানে fetch/xhr রিকোয়েস্টের দিকে তাকান তাহলে দেখবেন আপনি কোনো লাইক, কমেন্ট বা কিছু করলে ওখানে কিছু xhr রিকোয়েস্ট যাবে। সেগুলোতে ক্লিক করলে JSON আকারে API দেখতে পারবেন। এখন আপনি তো এই এপিআই ব্যবহার করছেন না। আপনি করছেন ফেসবুকে ফ্রন্টএন্ড ব্রাউজার বা মোবাইল অ্যাপের মাধ্যমে। সেই ব্রাউজার বা মোবাইল অ্যাপ ঐ এপিআই ব্যবহার করছে। তাহলে আপনি বা ইউজার কখনই ক্লায়েন্ট না, ক্লায়েন্ট হলো তারাই যারা এপিআই সরাসরি ইউজ করছে। + +আমরা একটু আমাদের ক্লায়েন্ট কি কি হতে পারে সেটা লিস্ট করি। + +1. Web App: আমরা ব্রাউজারের মাধ্যমে অ্যাপ ব্যবহার করলেও আমাদের ব্যাকএন্ডের একচুয়েল ক্লায়েন্ট হলো এর Web অ্যাপ। আমাদের Web অ্যাপই ব্যাকএন্ডের এপিআই ব্যবহার করছে সরাসরি। +2. Mobile App: আমরা ব্রাউজারে ফেসবুকের ওয়েব অ্যাপ্লিকেশনে একটা পোস্ট দিলে তার নোটিফিকেশন আমাদের মোবাইল অ্যাপেও চলে আসে। কিন্তু আমি তো দিয়েছি ওয়েব অ্যাপ্লিকেশনে, মোবাইলে কেন নোটিফিকেশন আসবে? তার মানে ওয়েব, ডেস্কটপ, মোবাইল অ্যাপ্লিকেশন একটা নির্দিষ্ট কিছুর সাথে কানেক্টেড, সেটা হলো এপিআই। তাই মোবাইল অ্যাপও আমাদের ব্যাকএন্ডের ক্লায়েন্ট। +3. Desktop App +4. IoT Device: IoT (Internet of Things) ডিভাইস হলো এমন কিছু ছোট ছোট ননস্ট্যান্ডার্ড কম্পিউটিং ডিভাইস, যারা ওয়্যারলেসের মাধ্যমে ইন্টারনেটের সাথে কানেক্টেড থাকে এবং ডাটা পাঠানোর ক্ষমতা রাখে। যেমন স্মার্ট টিভি। স্মার্ট টিভিতে আমরা ইন্টারনেটের মাধ্যমে ইউটিউব, ফেসবুক, নেটফ্লিক্স এসব চালাতে পারি। ধরেন আমাদের একটা IoT ডিভাইস আছে যেটা শুধু দরজা খুললে আর বন্ধ করলে এই ইনফরমেশন সার্ভারের কাছে ডাটা পাস করবে। যেহেতু এই ডিভাইস ব্যাকএন্ড অ্যাপ্লিকেশনের এপিআই ব্যবহার করছে তাই IoT ডিভাইস আমাদের ক্লায়েন্ট। + +সহজ কথায় যেকোনো অ্যাপ্লিকেশন বা ডিভাইস যে আমাদের ব্যাকএন্ডের এপিআই ব্যবহার করছে, তাকেই আমরা আমাদের ব্যাকএন্ডের ক্লায়েন্ট বলে গণ্য করতে পারি। তাহলে এই থিওরি অনুসারে আমাদের ডাটাবেজের ক্লায়েন্ট হলো ব্যাকএন্ড। কারণ আমাদের ডাটাবেজ সরাসরি ব্যবহার করে ব্যাকএন্ড। + +এখন সবাই কিভাবে ব্যাকএন্ডের সাথে কানেক্ট হচ্ছে। সেই মাধ্যম হলো এপিআই। কারণ সে সবাইকে JSON আকারে ডাটা প্রোভাইড করছে। ডাটাবেজে কিভাবে ডাটা সেইভ হচ্ছে সেটা ফ্রন্টএন্ডের ব্যাপার না। ফ্রন্টএন্ড সবসময় ব্যাকএন্ড থেকে JSON ব্যবহার করছে। যেহেতু ব্যাকএন্ড সবাইকেই একই JSON দিচ্ছে সুতরাং যে এই JSON বুঝতে পারে, সেই এর ক্লায়েন্ট হওয়ার যোগ্যতা রাখে। + +আরেকটু ভালভাবে বুঝার চেষ্টা করি। ধরেন একজন দোকানদার চাল বিক্রি করে। এখন সবাই ঐ দোকানে যায় চাল কিনতে, কেউ ৫ কেজি কিনে, কেউ ২৫ কেজির বস্তা কিনে, কেউ ৫০ কেজির বস্তা কিনে। কিন্তু আল্টিমেটলি সবাই চালই কিনে। তাহলে দোকানদারকে যদি আমরা ব্যাকএন্ড এপিআই ধরি তাহলে তার ক্লায়েন্ট যারা চাল কিনে সবাই। কেনার ওয়ে ভিন্ন হতে পারে, পরিমাণ ভিন্ন হতে পারে কিন্তু চালই কিনছে সবাই। + +এবার আমরা যে কফিশপের অ্যাপ্লিকেশনের প্ল্যানিং করেছিলাম সেটা ধরি। এর ব্যাকএন্ডে আমরা রেস্ট এপিআই বানালাম। এবার যারা প্রোগ্রামার তাদের বেশিরভাগ সময় কাটে ভিএস কোডে। আমরা চাইলে ভিএস কোডে একটা এক্সটেনশন বানিয়ে ফেললাম। সেই এক্সটেনশন থেকে অর্ডার করা যাবে। সেই এক্সটেনশন বানানোর প্রসেস ভিন্ন হতে পারে, কিন্তু আমরা ঐ ব্যাকএন্ডের এপিআই ব্যবহার করবো। সুতরাং ভিএস কোড এক্সটেনশন আমার ব্যাকএন্ডের ক্লায়েন্ট। এবার যাদের বেশির ভাগ সময় কাটে ব্রাউজারে তাদের জন্য আমরা ক্রোমিয়াম এক্সটেনশন বানিয়ে ফেললাম। যেহেতু এখন ম্যাক্সিমাম ব্রাউজার ক্রোমিয়াম ব্যবহার করে তাই ক্রোমিয়াম এক্সটেনশন বানালে তা সব ব্রাউজার সাপোর্ট করবে। এখন এই এক্সটেনশন বানানোর সিস্টেম আলাদা। কিন্তু এটা ব্যবহার করবে আমাদের সেই একই এপিআই। ব্যাকএন্ড কিন্তু একটাই। তাহলে এই ক্রোমিয়াম এক্সটেনশনও আমাদের ব্যাকএন্ডের ক্লায়েন্ট। আবার যারা ডিজাইনার তারা কাজ করেন ফিগমাতে। ফিগমা প্লাগিনও বানাতে পারি আমরা এই সেইম এপিআই ব্যবহার করে। আসল কথা হচ্ছে আমার ক্লায়েন্ট অনেক অনেক হতে পারে। কিন্তু সবার ব্যাকএন্ড একটাই। ব্যাকএন্ড কিন্তু আমরা চেইঞ্জ করছি না। আমরা এক এক উপায়ে এক এক ক্লায়েন্টের কাজ করছি। কিন্তু ব্যাকএন্ড সেই একটাই। তার মানে আমরা একটা অ্যাপ্লিকেশনের ওয়েব, মোবাল, ডেস্কটপ, এক্সটেনশন, প্লাগিন যাই বানাই না কেন আমাদের ব্যাকএন্ড কিন্তু একটাই। ঐ ব্যাকএন্ডের মাধ্যমেই আমাদের সমস্ত ভার্সন কানেক্টেড থাকে। + +এখন প্রশ্ন উঠতে পারে আমাদের তো শুধু ওয়েব অ্যাপ ডেভেলপমেন্ট শিখলেই হচ্ছে এতকিছু জানার প্রয়োজন কি? আমরা যা যা বললাম, IoT ডিভাইস ছাড়া বাকি সবগুলোতেই কিন্তু ইউজার কিছু না কিছু ইন্টেরেকশন করছে অ্যাপ্লিকেশনের মাধ্যমে। এই সবগুলোই কিন্তু ফ্রন্টএন্ডের অন্তর্ভুক্ত। যেখানে যেখানে UI এবং ইউজার ইন্টেরেকশনের ব্যাপার আছে সেগুলো সবগুলোই ফ্রন্টএন্ডের অন্তর্ভুক্ত। শুধু তা বানানোর সিস্টেমটা এক একটার ক্ষেত্রে একেকরকম। কিন্তু ফ্রন্টএন্ডের মূল কনসেপ্ট এক। কনসেপ্ট যদি আমাদের জানা থাকে তাহলে আমরা যেকোনো কিছুর জন্য অ্যাপ্লিকেশন বানাতে পারবো। + +## Features of Frontend + +এখন আমরা বুঝলাম ফ্রন্টএন্ড কি। এখন এই ফ্রন্টএন্ডের মধ্যে থাকে কি? চলুন সেগুলো আলোচনা করি। + +- Representation Layer: এটা বলতে বুঝায় আমাদের অ্যাপ্লিকেশন ওপেন করলে যা আমাদের চোখের সামনে আসে। সেটা একটা বাটন বা ফর্ম বা প্যারাগ্রাফ, হেডিং, ইমেজ ইত্যাদি যাই হোক না কেন। অ্যাপ্লিকেশনের ইউজার ইন্টারফেইসকেই আমরা বলি রিপ্রেজেন্টেশন লেয়ার। HTML, CSS দিয়ে আমরা এই লেয়ারটা তৈরি করি। +- Data Layer: দুই ধরণের ডাটা আছে - অ্যাপ্লিকেশন ডাটা ও সার্ভার ডাটা। আমরা ধরেন কোনো স্ক্রিনকে ফুলস্ক্রিন করবো একটা জায়গায় ক্লিক করে, বা ভিএস কোডে আমাদের যে সাইড বার আছে সেটাকে আমরা Toggle করতে পারি এসব অ্যাপ্লিকেশন রিলেটেড ডাটা। এই ডাটাগুলো সার্ভারে রাখার কোনো প্রয়োজন নেই। এগুলো আমরা আমাদের ফ্রন্টএন্ডে রাখতে পারি। আর সার্ভার ডাটা হলো যেগুলো আমরা এপিআই থেকে পাবো, যেগুলো আমাদের ডাটাবেজে স্টোর হয়ে থাকে। এখন ডাটাগুলো কোনো না কোনো উপায়ে আমাদের অ্যাপ্লিকেশনে প্রসেস করার বা ম্যানেজ করার বিষয় আছে। এগুলো করার জন্য এই ডাটা লেয়ারের প্রয়োজন। +- Logical Layer: আমরা ডাটা লেয়ার থেকে ডাটা নিয়ে কিছু লজিকের মাধ্যমে প্রসেস করে সেটা রিপ্রেজেন্টেশন লেয়ারে পাঠাবো। আবার এর উল্টোও হতে পারে। রিপ্রেজেন্টেশন লেয়ার থেকে ডাটা নিয়ে তা প্রসেস করে আবার ডাটা লেয়ারে পাঠাতে পারি। এই প্রসেসটা যেখানে হয় সেটা লজিক্যাল লেয়ার। +- Network Layer: নেটওয়ার্ক লেয়ারের কাজ হলো লজিক্যাল লেয়ারের সাথে সম্পৃক্ত থেকে যে ডাটাগুলো সার্ভারের সাথে কমিউনিকেশন দরকার সেই কমিউনিকেশন রক্ষা করা। এই একটা মাত্র লেয়ার যে লেয়ারের মাধ্যমে আমরা ব্যাকএন্ড এবং ফ্রন্টএন্ডকে যুক্ত করবো। + +এগুলোই মূলত আমাদের ফ্রন্টএন্ডের লেয়ার। মূলত চার ধরণের লেয়ার। রিপ্রেজেন্টেশন লেয়ার আমরা এইচটএমএল, সিএসএস দিয়ে তৈরি করবো। লোকাল স্টোরেজ, বা ছোট ছোট কিছু ডাটাবেজ নিয়ে আমরা ডাটা লেয়ারে কাজ করতে পারি। নেটওয়ার্ক নিয়ে কাজ করার জন্য আছে নেটওয়ার্ক লেয়ার। আর সবগুলোকে সমন্বয় করার জন্য আছে লজিক্যাল লেয়ার। আমরা একটা গ্রাফিক্যাল রিপ্রেজেন্টেশনের মাধ্যমে এটা দেখানোর চেষ্টা করলাম। + +![Forntend Layer](./images/Frontend-Layer.jpg) + +আমরা ব্যাকএন্ড এপিআই থেকে নেটওয়ার্ক লেয়ারের মাধ্যমে ডাটা নিয়ে সেটা লজিক্যাল লেয়ারের মাধ্যমে সুন্দরভাবে প্রসেস করে তা ডাটা লেয়ারে জমা রাখবো যেন প্রয়োজনে সেই ডাটা আমরা রিপ্রেজেন্টেশন লেয়ারে শো করতে পারি। আবার রিপ্রেজেন্টেশন লেয়ার থেকে প্রাপ্ত ডাটা লজিক্যাল লেয়ারের মাধ্যমে প্রসেস করে নেটওয়ার্ক লেয়ারের মাধ্যমে ব্যাকএন্ডে স্টোর করে রাখতে পারি আবার ব্যাকএন্ড থেকে ডাটা নিয়ে আসতে পারি। নেটওয়ার্ক লেয়ার আর ব্যাকএন্ড এপিআই এর মধ্যে কমিউনিকেশন হয় HTTP দ্বারা। এটাই হচ্ছে মূল কনসেপ্ট। + +এই কনসেপ্ট যত ফ্রন্টএন্ড অ্যাপ্লিকেশ আছে সবকিছুর জন্য সেইম। আমরা এই কনসেপ্ট দিয়ে যেকোনো ফ্রন্টএন্ড অ্যাপ বানাতে পারবো। এখানে পার্থক্য বলতে শুধু একেক অ্যাপ্লিকেশনের জন্য ল্যাঙ্গুয়েজ, ফ্রেমওয়ার্ক এগুলো। যেমন যদি আমরা রিয়্যাক্টে কাজ করি তাহলে আমরা লিখবো jsx, যদি ভিউ বা অ্যাঙ্গুলারে কাজ করি তাহলে টেমপ্লেট ইঞ্জিন ব্যবহার করবো, যদি জাভা কটলিন দিয়ে মোবাইল অ্যাপ ডেভেলপ করি সেখানে আমাদের লিখতে হবে xml। কিন্তু দিনশেষে আমরা ঐ রিপ্রেজেন্টেশন লেয়ারই তৈরি করছি। তাই ইমপ্লিমেন্ট ভিন্ন হলেও আমাদের কনসেপ্ট সবকিছুর জন্য একই। + +একটা সময় একই ব্যাকএন্ড ইউজ করে সব ফ্রন্টএন্ড করা যেতো না। কারণ তখন ব্যাকএন্ডে একটা রিকোয়েস্ট আসলে সেটা ঐ ডাটাসহ একটা পেইজের HTML তৈরি করে ব্যাক করতো। এখন একটা বিশাল এইচটিএমএল ফাইল থেকে মোবাইল অ্যাপের মতো করে ডাটা বের করে আনা অনেক কষ্টসাধ্য ছিল। সে সমস্যার সমাধান করেছে JSON। এখন ব্যাকএন্ড এপিআই আমাদের শুধু raw data দিয়ে দিচ্ছে। আমরা আমাদের মতো করে সেই ডাটা ইউজ করে ফ্রন্টএন্ড অ্যাপ্লিকেশন বানাতে পারছি। + +আরেকটা বাস্তব উদাহরণ দিয়ে আমরা এটা বুঝার চেষ্টা করি। আগে কম্পিউটার কোম্পানিগুলো ডেস্কটপ বানিয়ে রাখতো। অর্থাৎ তারা আগে থেকে কনফিগার করে রাখতো সব। আমরা গিয়ে জাস্ট কিনে নিয়ে চলে আসতাম। ঐ যে পুরো এইচটিএমএল ফাইলের মতো। আমাদের আলাদাভাবে কনফিগার করা সম্ভব হতো না। এখন আমরা চাইলে একটা ডেস্কটপের মধ্যে বিভিন্ন কোম্পানির কম্পোনেন্ট ইউজ করতে পারি। আমরা আমাদের মতো করে হার্ডডিস্ক, র‍্যাম, গ্রাফিক্স কার্ড, মাদারবোর্ড সব কনফিগার করতে পারি। রেস্ট এপিআই বর্তমানে এই সুবিধাটাই দিচ্ছে। আমাদের যা লাগবে আমরা শুধু সেই ডাটা নিয়েই কাজ করতে পারবো। + +এত এত কথাকে এবার সংক্ষিপ্ত করে এক কথা দিয়ে বুঝিয়ে দিই। ধরেন আপনি কোথাও আপনার অ্যাপ্লিকেশনটা ওপেন করলেন। সেটা মোবাইল, ব্রাউজার যেটাই হোক। করার পরে আপনি কিছু সার্চ করতে চাইছেন। যেখানে আপনি আপনার সার্চ করার জন্য লিখবেন সেটা হচ্ছে **রিপ্রেজেন্টেশন লেয়ার**। এবার সেই সার্চ করা ডাটা খুঁজে নিয়ে আসতে হবে সার্ভার থেকে। সার্ভারের সাথে সেই কমিউনিকেশনের দায়িত্ব পালন করবে **নেটওয়ার্ক লেয়ার**। এবার আমার ডাটাটা একটু ফরম্যাটিং এর প্রয়োজন হতে পারে। সেটা করবে **লজিক্যাল লেয়ার**। এরপর সেটা ক্লায়েন্টের একটা জায়গায় স্টোর করে রাখতে হবে। সেই কাজটার জন্য আছে **ডাটা লেয়ার**। এবার ডাটা লেয়ার লজিক্যাল লেয়ারের মাধ্যমে আমাদের সার্চ করা ডাটা সুন্দরভাবে যেভাবে শো করানো দরকার সেভাবে রিপ্রেজেন্টেশন লেয়ার পাঠিয়ে দেবে এবং রিপ্রেজেন্টেশন লেয়ার সেই ডাটা আমাদেরকে শো করবে। + +এতটুকু যদি আপনারা বুঝতে পারেন তাহলে ফ্রন্টএন্ড ডেভেলপমেন্টের একটা বড় অংশ আপনারা বুঝে ফেলেছেন। যখন ফ্রেমওয়ার্কে কাজ করবো আমরা তখন অনেক মজা পাবেন কাজ করতে যদি এই কনসেপ্ট বুঝে থাকেন। + +আরেকটা জিনিস একটু বুঝা দরকার। এই পুরো প্রসেসে মেইন রোল প্লে করে HTTP। এটা না থাকলে আমাদের এই প্রসেস সম্পন্ন হতো না। HTTP ছাড়া আমরা ব্যাকএন্ড থেকে ডাটা নিয়ে আসতে পারবো না। যেখানে যে ক্লায়েন্টের কাছে HTTP আছে সে চাইলে ব্যাকএন্ড থেকে ডাটা নিয়ে আসতে পারবে। + +সবসময় কি ফ্রন্টএন্ডই ক্লায়েন্ট হিসেবে কাজ করবে? না, আমাদের ব্যাকএন্ডও কখনও কখনও ফ্রন্টএন্ডের মতো কাজ করবে। আমরা একটু নিচের ফিগারটা দেখি। + +![backend-client](./images/backend-client.jpg) + +ধরেন আমাদের ব্যাকএন্ড অ্যাপ্লিকেশন আরো তিনটা সার্ভার ইউজ করে। Twilio, SendGrid, Google Map। এখন এই ক্ষেত্রে আমাদের ব্যাকএন্ড অ্যাপ্লিকেশন হচ্ছে ক্লায়েন্ট। কারণ সে অন্য তিনটা সার্ভার থেকে ডাটা নিয়ে আসছে। সেটা পসিবল হচ্ছে HTTP এর মাধ্যমে। HTTP বাদেও আরো সিস্টেম আছে। সেগুলো আপাতত আমাদের মাথায় রাখার দরকার নেই। আমরা আগে প্রসেসটা বুঝি। + +এখানে যে চ্যানেলের মাধ্যমে কমিউনিকেশন হচ্ছে সেটা হচ্ছে HTTP আর যে ফরম্যাটের মাধ্যমে ডাটা আদান প্রদান হচ্ছে সেটা হচ্ছে JSON। + +এবার আমরা যেহেতু ওয়েব ডেভেলপমেন্টে আছি আমাদের জন্য অপশন কি কি আছে? আমাদের জন্য অপশন হচ্ছে JavaScript এবং DOM (Document Object Model)। এই দুইটা জিনিস ব্যবহার করেই আমরা সব কাজ করে থাকি। + +## Why Frameworks + +এখন জিজ্ঞেস করতে পারেন তাহলে এত এত ফ্রেমওয়ার্ক আসলো কেন? আসলো তার কারণ প্রথমত ডম ম্যানিপুলেশন অনেক ব্যয়বহুল। টাকার দিক দিয়ে না। একেকটা ডম আপডেট করা, ম্যানিপুলেট করার জন্য প্রচুর টাইম এবং মেমোরি লাগে। মানে টাইম কমপ্লেক্সিটি এবং মেমোরি কমপ্লেক্সিটি বেশি অনেক। আরেকটা বড় কারণ হচ্ছে আমরা জানিনা আমাদের ইউজার কোন পিসি ব্যবহার করছে, কতো হাই কনফিগারেশন পিসি ব্যবহার করছে। আমাদের লো কনফিগারেশনের পিসি ব্যবহারকারীও থাকবে, হাই কনফিগারেশনের পিসি ব্যবহারকারীও থাকবে। এখন আমরা যদি লো কনফিগারেশনের পিসিতে চালানোর মতো অ্যাপ্লিকেশন বানাতে না পারি তাহলে আমাদের অ্যাপ্লিকেশন মার্কেট পাবেনা। কারণ লো কনফিগারেশনের পিসির ইউজার বেশি। এখন আমাদের চিন্তা থাকতে হবে কিভাবে আমরা লো কনফিগারেশনের পিসির জন্য হাই পারফরম্যান্স অ্যাপ্লিকেশন বানাতে পারি সেটা নিয়ে। যদি এরকম বানাতে হয় তখন সেটা হয়ে যায় কমপ্লেক্স একটা টাস্ক। আরো এক লেয়ার কমপ্লেক্সিটি বৃদ্ধি পায় যখন সেটা হয় ওয়েব অ্যাপ্লিকেশন। কারণ আমাদের ওয়েব অ্যাপ্লিকেশন ভিজিট করবে মানুষ ব্রাউজার ব্যবহার করে। দুনিয়াতে অসংখ্য ব্রাউজার আছে। কেউ ব্যবহার করছে পুরাতন ভার্সন, কেউ লেটেস্ট, কেউ ক্রোম, কেউ এইজ, কেউ ফায়ারফক্স। একেকজনের কনফিগারেশন একেকরকম। আরো একটা সমস্যা হলো স্ক্রিন সাইজ। কারো স্ক্রিন সাইজ ছোট, কারো বড়, কেউ মোবাইল থেকে ব্যবহার করে, কেউ ট্যাব ইত্যাদি। এখন যদি আমরা সব মাথায় রেখে ভ্যানিলা জাভাস্ক্রিপ্ট দিয়ে অ্যাপ্লিকেশন বানাতে চাই তাহলে আমাদের অনেক সময় লাগবে, অনেক অ্যাডিশনাল কোড লিখতে হবে, এগুলো ম্যানেজ করতে প্রচুর সময় চলে যাবে এবং সবচেয়ে বিরক্তিকর বিষয় যেটা তা হলো পারফরম্যান্স অপটিমাইজ করতে গিয়ে প্রচুর সময় চলে যাবে। + +এই সমস্যাগুলোর সমাধান হচ্ছে ফ্রেমওয়ার্ক। যেমন রিয়্যাক্ট, ভিউ, অ্যাঙ্গুলার ইত্যাদি। + +তাহলে এত ফ্রেমওয়ার্ক কেন? একটা হলেই তো হয়ে যেতো। বাজারের এক একটা টুলস আসার জন্য কোনো না কোনো উদ্দেশ্য থাকে। কোনো একটা প্রব্লেম সলভ করার জন্য এক এক টুলস বাজারে আসে। এখন একটা প্রব্লেম সলভ করার জন্যও ভিন্ন ভিন্ন টুলস আসতে পারে। দেখা যাচ্ছে এদের মধ্যে কোনোটার পারফরম্যান্স ভাল। আবার একেকজন একেক টুলসে স্বাচ্ছন্দ্যবোধ করে। যেমন রিয়্যাক্ট, ভিউ আর অ্যাঙ্গুলারের মধ্যে যদি পারফরম্যান অনুযায়ী বিচার করা হয় তাহলে চোখ বন্ধ করে বলা যায় অ্যাঙ্গুলারের পারফরম্যান্স সবচেয়ে ভাল। কারন এরা শ্যাডো ডম ইউজ করে। কিন্তু এদের লার্নিং কার্ভটা অনেক বড়। এটা অতটা ফ্লেক্সিবল না। এখানে যা যা দেয়া আছে সেভাবেই কাজ করতে হবে ইন্ডাস্ট্রিতে এখনও অ্যাঙ্গুলারের ব্যবহার রয়েছে। আমরা যদি [2021 state of js](https://2021.stateofjs.com/en-US/libraries/front-end-frameworks) এ যায় দেখবো অ্যাঙ্গুলারের ব্যবহার দিন দিন বেড়েই চলেছে। + +![2021stateofjs](./images/2021stateofjs.png) + +অ্যাঙ্গুলার সাধারণত ব্যবহার করা হয় সেসব অ্যাপ্লিকেশনে যেখানে অনেক কমপ্লেক্স ব্যাপার রয়েছে। যেখানে কোড ম্যানেজ করাটাই একটা চ্যালেঞ্জ। সেসব জায়গায় আমার ফ্লেক্সিবিলিটি দেখানোর কোনো মানে নেই। সেখানে দরকার প্রোপার একটা গাইডলাইন যা ফলো করে আমরা আমাদের অ্যাপ্লিকেশন ম্যানেজ করতে পারবো। সেই জায়গায় আমাদের দরকার অ্যাঙ্গুলার। + +রিয়্যাক্ট কোনো গাইডলাইন নিজে থেকে দিয়ে দেয়নি। এখন বড় কোনো অ্যাপ্লিকেশন বানাতে হলে আমাদের টীমের জন্য নিজের থেকে একটা গাইডলাইন বানাতে হবে। তাই বড় বড় অ্যাপ্লিকেশনের ক্ষেত্রে অ্যাঙ্গুলার অনেক ভাল কাজ দেয়। + +এখন শেখার জন্য যদি বলতে হয় তাহলে রিয়্যাক্ট ইজি। রিয়্যাক্টের লার্নিং কার্ভটা তুলনামূলক ছোট। এখন এই লার্নিং কার্ভ বলতে কি বুঝানো হচ্ছে? এখানে বুঝানো হচ্ছে অ্যাঙ্গুলারে কিছু কিছু রুলস তৈরি করে দিয়েছে যার বাইরে যাওয়ার কোনো সুযোগ নেই। সবকিছু শিখে আসতে একটু সময় বেশি লাগে। ভিউতেও প্রায় সবকিছু বিল্টইন। কিন্তু কিছু কিছু ক্ষেত্রে ফেক্সিবিলিটি দিয়েছে। এক্ষেত্রে রিয়্যাক্ট পুরোই ফ্লেক্সিবল। যে জাভাস্ক্রিপ্ট জানে তার জন্য রিয়্যাক্ট শিখতে বেশিদিন লাগে না। তাই শেখার জন্য রিয়্যাক্ট বেস্ট। + +## Core Features of ReactJS + +এবার আমরা একটু রিয়্যাক্ট নিয়ে সামান্য আলোচনা করি। আমাদের ঘুরেফিরে ঐ চারটা কাজই করতে হবে। এটার জন্য আপাতত আমরা রিয়্যাক্ট বেছে নিচ্ছি। আমরা এরপর আমাদের প্রয়োজন অনুসারে যেকোনো ফ্রেমওয়ার্ক নিয়ে কাজ করতে পারবো। + +এখন যেহেতু রিয়্যাক্ট বেছে নিচ্ছি সেটা শেখার জন্য কি কি টুলস লাগবে, কি কি কাজ আছে, কি কি শিখতে হবে তার একটা ওভারভিউ এখানে দেয়ার চেষ্টা করা হলো। + +আমাদের কি কি কোর কনসেপ্ট শিখতে হবে তার একটা লিস্ট দেয়া হলো - + +- Component: রিয়্যাক্টের সবকিছুই কম্পোনেন্ট। বাকি যা কিছু আছে সব এই কম্পোনেন্টকে কেন্দ্র করেই। রিয়্যাক্টে কম্পোনেন্টকে আমরা দুই ভাগে ভাগ করতে পারি। + + - Stateful: যে কম্পোনেন্টে আমরা ডাটা নিয়ে কাজ করতে পারি সেটা হলো Stateful কম্পোনেন্ট। ডাটার আরেকটা নাম হলো স্টেট। + - Stateless: যে কম্পোনেন্টে আমাদের ডাটা নিয়ে কোনো কাজ নাই সেটা হলো স্টেটলেস কম্পোনেন্ট। + + এই দুইটা কম্পোনেন্ট আবার দুইভাবে তৈরি করা যায়। + + - Class Component: এখন অনেকেই প্রশ্ন করতে পারেন ক্লাস কম্পোনেন্ট শেখার দরকার কি? এখন তো ফাংশনাল কম্পোনেন্ট চলে আসছে। দুইটা কারণে দরকার আছে ক্লাস কম্পোনেন্ট শেখার। প্রথমত এখনও ৯০% ইন্টারভিউতে ক্লাস কম্পোনেন্ট নিয়ে প্রশ্ন করা হয়। কেন করা হয়। কারণ ফাংশনাল কম্পোনেন্ট নতুন এসেছে। তাই ফাংশনাল কম্পোনেন্টের প্রশ্ন দিয়ে জাজ করা যাবে না আপনার রিয়্যাক্টের এক্সপেরিয়েন্স কতটুকু। তাই আপনার এক্সপেরিয়েন্স কতটুকু তা জাজ করার জন্য ক্লাস কম্পোনেন্ট থেকে প্রশ্ন করা হয়। দ্বিতীয়ত এখনও এমন প্রচুর প্রজেক্ট রয়েছে যেগুলো পুরোপুরি ক্লাস কম্পোনেন্ট দিয়ে করা হয়েছে। হয় তারা মাইগ্রেট করে ফাংশনাল কম্পোনেন্টে আসছে, নয়তো তাদের কোডবেইজ যেভাবে আছে তারা সেভাবেই তা ম্যানেজ করছে। সেরকম কোনো প্রজেক্ট যদি আপনার কাছে আসে তাহলে তো অবশ্যই আপনার ক্লাস কম্পোনেন্ট নিয়ে জানা থাকতে হবে। আরেকটা ছোট কারণ হচ্ছে, ৯৯% কাজ আপনি ফাংশনাল কম্পোনেন্ট দিয়ে করে ফেলতে পারলেও ১% কাজ আপনি পারবেন না। সেটা হচ্ছে এরর বাউন্ডারি রিলেটেড কাজ। সেই কাজটা করার জন্য আপনার অবশ্যই ক্লাস কম্পোনেন্ট ব্যবহার করতে হবে। আর এরর বাউন্ডারি অনেক বিশাল একটা বিষয়। এটাকে এড়িয়ে যাওয়ার কোনো সুযোগ নেই। সুতরাং এখনও পর্যন্ত ক্লাস কম্পোনেন্টকে অ্যাভয়েড করার কোনো সুযোগ নেই। ক্লাস কম্পোনেন্টের সাথে একটা গুরুত্বপূর্ন বিষয় আছে। সেটা হলো - + - Lifecycle: এর মানে হলো ক্লাস কিভাবে চলবে না চলবে। একটা কম্পোনেন্ট কিভাবে শুরু হবে, কিভাবে প্রসেস হবে, কিভাবে আপডেট হবে, কিভাবে ডিলিট হবে, কখন ডিলিট হবে, ডিলিট হওয়ার সময় আমরা কিছু করতে পারবো কিনা, আপডেট হওয়ার সময় আমরা কিছু করতে পারবো কিনা সেই জিনিসগুলো হ্যান্ডেল করার কাজ করে লাইফ সাইকেল। ক্লাস কম্পোনেন্ট আর লাইফসাইকেল ম্যানেজ করে আগে কিছু কম্পোনেন্ট প্যাটার্ন তৈরি করা হয়েছে যেগুলো বেশ জটিল। + - Functional Component: এখানে একটা মজার বিষয় আছে। সেটা হলো - + + - Hooks: এর কাজ কি? এর কাজ হচ্ছে কম্পোনেন্ট প্যাটার্নগুলো ব্যবহার করে যা যা করা হতো সেটা এখন এক জায়গায় চলে গেছে যেটা হলো hooks। এটা খুবই মজার একটা বিষয়। যখন আমরা শিখবো তখন বুঝতে পারবো। যেরকম ক্লাস কম্পোনেন্ট আর লাইফসাইকেল মিলে কম্পোনেন্ট প্যাটার্ন এসেছিল, সেরকম ফাংশনাল কম্পোনেন্ট আর hooks মিলে এসেছে কাস্টম হুকস। যে কাস্টম হুকস দিয়ে সমস্ত কম্পোনেন্ট প্যাটার্নকে রিপ্লেস করে দেয়া হচ্ছে। যার ফলে এটা অনেক সহজ হয়ে গেছে। + + - Local State Management: ক্লাস বা ফাংশনাল যেকোনো কম্পোনেন্ট দিয়েই স্টেট ম্যানেজমেন্ট করার যায়। লোকাল স্টেট ম্যানেজমেন্ট বলতে বুঝাচ্ছে একটা কম্পোনেন্টের মধ্যে থাকা স্টেট বা ডাটা। + + - Component Tree: যেহেতু আমরা রিয়্যাক্টে সবকিছু কম্পোনেন্ট হিসেবে ধরে নিচ্ছি, একটা ছোট UI যেটা সেটাও একটা কম্পোনেন্ট আবার প্যারেন্ট UI যে সব দেখাচ্ছে সেও একটা কম্পোনেন্ট। এই প্যারেন্ট চাইল্ডের মধ্যে কোনো না কোনোভাবে কমিউনিকেশন হচ্ছে। সেটাকে আমরা বলছি কম্পোনেন্ট ট্রী। + + - State Lifting: Parent থেকে চাইল্ডে সহজেই ডাটা পাঠানো যায়, কিন্তু চাইল্ড থেকে প্যারেন্টে সহজে ডাটা পাঠানো যায় না। তো সেখানে আমাদের একটা বিষয় আছে। সেটা হলো স্টেট লিফটিং। + +- JSX: এটার মাধ্যমে আমরা রিয়্যাক্টে UI শো করি। এর মধ্যে কিছু বিষয় আছে। + - Conditional Rendering: আমার ইউজার যদি লগইন অবস্থায় থাকে তাহলে এক ধরণের ন্যাভবার দেখাবে আর যদি লগআউট অবস্থায় থাকে তাহলে আরেক ধরণের ন্যাভবার দেখাবে। যদি লগইন অবস্থায় থাকে তাহলে লগআউট বাটন দেখাবে, যদি লগআউট অবস্থায় থাকে তাহলে লগইন বাটন দেখাবে। এসমস্ত বিষয়বস্তু কন্ডিশনাল রেন্ডারিং এর অন্তর্ভুক্ত। + - List Rendering: আমাদের অনেক সিমিলার টাইপের ডাটা আছে। সেগুলো নিয়ে কিভাবে কাজ করতে পারি তা এই অংশের অন্তর্ভুক্ত। + - Forms: কিভাবে আমরা গুরুত্বপূর্ণ ফর্ম নিয়ে কাজ করবো সেটাও একটা শেখার ব্যাপার আছে। + - Event Handling: ইভেন্ট হ্যান্ডলিং নিয়ে আমাদের জানতে হবে। + +মোটামুটি এগুলোই কোর কনসেপ্ট রিয়্যাক্টের। শুধু রিয়্যাক্ট না যেকোনো ফ্রন্টএন্ড অ্যাপ্লিকেশনের কনসেপ্ট। যদি এর বাইরে কিছু থাকে তা আমরা কাজ করতে করতে শিখে নিবো। পুরো প্রসেসকে একটা ডায়াগ্রামের মাধ্যমে দেখানো হলো। + +![React Learning Curve](./images/react-overview.jpg) + +রিয়্যাক্টের কোর কনসেপ্ট নিয়ে একটা ধারণার জন্য [Understand React JS Core Features](https://www.youtube.com/playlist?list=PL_XxuZqN0xVBANld2gDEE6_0G886zavUs) প্লেলিস্টটা আপনাদের শেষ করতে হবে। + +পুরো ডায়াগ্রাম [Client Server](./Client%20Server.drawio) এ পেয়ে যাবেন। ভিএস কোডে drawio ফাইল দেখার জন্য একটা এক্সটেনশন আছে। যার নাম [Draw.io Integration](https://marketplace.visualstudio.com/items?itemName=hediet.vscode-drawio)। এটা ইনস্টল করে নিলে আপনারা সহজেই draw.io ফাইল ভিএস কোডেই ওপেন করতে পারবেন। + +![drawio](./images/drawio-ext.png) + +## Author + +[Aditya Chakraborty](https://github.com/adityackr) diff --git a/full-stack-army/class-overview/Lecture-27/images/2021stateofjs.png b/full-stack-army/class-overview/Lecture-27/images/2021stateofjs.png new file mode 100644 index 0000000..3b178be Binary files /dev/null and b/full-stack-army/class-overview/Lecture-27/images/2021stateofjs.png differ diff --git a/full-stack-army/class-overview/Lecture-27/images/Frontend-Layer.jpg b/full-stack-army/class-overview/Lecture-27/images/Frontend-Layer.jpg new file mode 100644 index 0000000..ec7887f Binary files /dev/null and b/full-stack-army/class-overview/Lecture-27/images/Frontend-Layer.jpg differ diff --git a/full-stack-army/class-overview/Lecture-27/images/backend-client.jpg b/full-stack-army/class-overview/Lecture-27/images/backend-client.jpg new file mode 100644 index 0000000..ee2b858 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-27/images/backend-client.jpg differ diff --git a/full-stack-army/class-overview/Lecture-27/images/drawio-ext.png b/full-stack-army/class-overview/Lecture-27/images/drawio-ext.png new file mode 100644 index 0000000..1a29a52 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-27/images/drawio-ext.png differ diff --git a/full-stack-army/class-overview/Lecture-27/images/react-overview.jpg b/full-stack-army/class-overview/Lecture-27/images/react-overview.jpg new file mode 100644 index 0000000..f12c026 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-27/images/react-overview.jpg differ diff --git a/full-stack-army/class-overview/Lecture-28/README.md b/full-stack-army/class-overview/Lecture-28/README.md new file mode 100644 index 0000000..f93d88b --- /dev/null +++ b/full-stack-army/class-overview/Lecture-28/README.md @@ -0,0 +1,9 @@ +# Lecture 28 [Frontend 1] - Frontend Course Planning & Discussion + +এই ক্লাসে বুটক্যাম্পের কোর্স আউটলাইন নিয়ে আলোচনা করা হয়েছে। যেহেতু এটা ফুলস্ট্যাক আর্মিতে পরে মার্জ হয়ে গিয়েছে সেহেতু এই আউটলাইন এখন দরকার নেই। তবে এই লেকচার থেকে অনেক কিছু জানতে পারবেন। তাই আপনারা ভিডিওটা দেখবেন এবং আপনাদের প্রয়োজনমতো ইনফরমেশন নিয়ে নিবেন। + +অ্যাপ্লিকেশন বানাতে হলে আগে অ্যাপ্লিকেশন ইউজ করতে জানতে হবে। তাহলে আপনি ফিচার সম্পর্কে জানতে পারবেন। আপনারা ৫টা অ্যাপ্লিকেশন ইউজ করে সেগুলোর ফিচার গুলো নোট করবেন। কোন ফিচারের কাজ কি, কোনটা নতুন দেখছেন, কোনটা আগে একরকম দেখেছেন এখন কিছুটা চেইঞ্জ পাচ্ছেন এসব। অ্যাপ্লিকেশন কি কি ইউজ করবেন? [Product Hunt](https://www.producthunt.com/) এ ঢুকে আপনারা অনেক অ্যাপ্লিকেশনের লিংক পাবেন। সেখান থেকে নিজের পছন্দমতো প্রোডাক্ট ব্যবহার করবেন আর ঐ প্রোডাক্ট সম্পর্কে একটা ডকুমেন্টেশন বানাবেন। + +## Author + +- [Aditya Chakraborty](https://github.com/adityackr) diff --git a/full-stack-army/class-overview/Lecture-29/README.md b/full-stack-army/class-overview/Lecture-29/README.md new file mode 100644 index 0000000..3c0b7f0 --- /dev/null +++ b/full-stack-army/class-overview/Lecture-29/README.md @@ -0,0 +1,434 @@ +# Lecture 29 [Frontend 2] - Understand React in A Different Way + +রিয়্যাক্ট নিয়ে জানতে গেলে আগে আমাদের জানতে হবে ফ্রন্টএন্ডের কাজটা কি। ফ্রন্টএন্ডের মূলত কাজ হলো দুইটা। + +- আমরা যে ডাটা ইউজারকে দেখাতে চাইছি সেটা শো করানো হেডিং, প্যারাগ্রাফ, ইমেজ ইত্যাদি ট্যাগের মাধ্যমে +- ইউজার যে ফিডব্যাক দিবে সেটাকে গ্রহণ করা ফর্মের মাধ্যমে। + +বর্তমানে ফ্রন্টএন্ড ডেভেলপমেন্ট কঠিন থেকে কঠিনতর হচ্ছে ইউজার ফিডব্যাক বা ইন্টের‍্যাক্টিভিটির কারণে। আমরা প্রথম অবস্থায় ভুলে যায় যে আমাদের ইউজার থেকে ডাটা নিতে হবে। আমাদের প্রাথমিক উদ্দেশ্য কিভাবে ডাটা শো করা যায় সেই ইঞ্জিনিয়ারিংটা বুঝা। এই ইউজারকে ডাটা শো করানোর জন্যই মূলত রিয়্যাক্ট বা অ্যাঙ্গুলারের মতো ফ্রেমওয়ার্কগুলো মার্কেটে জনপ্রিয়তা লাভ করেছে। আমরা একটু নিচের পেইজটা দেখি। + +![Product Hunt](./images/product_hunt.png) + +আমাদের যদি বলা হয় এই পেইজটা আমরা কিভাবে ডিজাইন করবো, তাহলে আমাদের উত্তর হবে এরকম। প্রথমে আমরা এইচটিএমএল ফাইলে মার্কআপ করবো এরপর সিএসএস লিখবো। শুরুতে ন্যাভবার তৈরি করবো। এরপর লেফট সেকশন আর রাইট সেকশন তৈরি করবো। এখানে প্রতিটা পেইজের জন্য আমরা আলাদা আলাদা এইচটিএমএল লিখবো। মোটামুটি প্রাথমিকভাবে আমরা এই সিদ্ধান্ত নিলাম। এটা হলো ওয়েব ডিজাইন। এটাকে ওয়েব ডেভেলপমেন্ট বলে না। + +ওয়েব ডেভেলপমেন্টে আমাদের চিন্তাটা অন্যরকম হতে হবে। আমাদের সূক্ষ্ম থেকে সূক্ষ্মতর বিষয় নিয়ে চিন্তা করতে হবে। ওয়েব ডেভেলপমেন্টের ক্ষেত্রে একটা কনসেপ্ট আছে। সেটা হলো [Atomic Design](https://bradfrost.com/blog/post/atomic-web-design/)। এই কনসেপ্টটা ভবিষ্যতে অনেকের কাজে লাগবে। আমাদের সবার প্রথমে বুঝতে হবে অ্যাটমিক ডিজাইন কি? কারণ আমরা যখন কোনো ওয়েব ডিজাইন করি তখন আমাদের উপর কিন্তু কোনো প্রেশার থাকে না। আমরা জানিনা এই টেমপ্লেট নিয়ে কিভাবে একজন ডেভেলপার তা ব্যবহার করবে। তাই আমরা যেখানে একই রকম কাজ আছে তা কপি পেস্ট করে করে বানিয়ে ফেলি। কিন্তু রিয়েল লাইফে সিম্পল একটা সার্চবারের জন্য ৫০০-৭০০ লাইনের কোড লেখা লাগে। এখন যারা ওয়েব ডিজাইনার তারা অবাক হতে পারেন কেন এত কোড লেখা লাগছে এই সামান্য জিনিসের জন্য। কারণ এখানে অ্যাক্সেসিবিলিটির ব্যাপার আছে, বিভিন্ন ফিচার্স আছে। যেমন সার্চ বারে ক্লিক করলে তা পুরো width জুড়ে ছড়িয়ে যাচ্ছে, আমরা Esc বাটন প্রেস করলে সেটা পূর্বের অবস্থায় ফিরে যাচ্ছে, যখন আমরা কিছু লিখছি প্রতিটা কীওয়ার্ড লেখার সাথে সাথে সে কিছু সাজেশন্স দেখাচ্ছে এরকম অনেক অনেক ফিচার্স আছে। যখন আমরা ওয়েবসাইট ডিজাইন করি আর যখন ডেভেলপ করি দুইটার মধ্যে আকাশ পাতাল পার্থক্য আছে। তাই যখন আমরা ওয়েব ডেভেলপ করি তখন আমাদের অনেক ছোট ছোট বিষয় নিয়ে ভাবতে হয় এবং এই ছোট ছোট বিষয়কে যতোই আমরা পুনরাবৃত্তি করতে পারবো ততোই আমরা একটা ভাল অ্যাপ্লিকেশনের আশা করতে পারবো। ধরেন আমার হোমপেইজে যে সার্চবার থাকবে ঠিক একই রকম সার্চবার অন্য কোনো পেইজে থাকবে। আমরা সেই ৭০০/৮০০ লাইনের কোড বারবার কপি পেস্ট না করে সেটাকে একবার লিখে তা রিইউজ করতে পারি। এই যে রিইউজের কনসেপ্ট, সেটার জন্যই ফ্রন্টএন্ডের যে ফ্রেমওয়ার্কগুলো সেগুলো অনেক জনপ্রিয় হয়েছিল। ক্লিক করার জন্য বা ইন্টের‍্যাকশনের জন্য না। আমরা একটা কম্পোনেন্ট একবার বানাবো। এরপর সেটা যতো জায়গায় লাগবে সে সে জায়গায় আমরা রিইউজ করবো। কিন্তু আমরা যখন ওয়েব ডিজাইন করি বা এইচটিএমএল সিএসএস দিয়ে কোড লিখি সেখানেও তো কপি পেস্ট করে করে রিইউজই করি। কপি পেস্টের মূল সমস্যা হলো, যদি কোনো এক জায়গায় চেইঞ্জ আছে, সেই কোড যদি আপনি ১০০০ জায়গায় পেস্ট করেন সেই ১০০০ জায়গায় গিয়ে গিয়ে চেইঞ্জ করা লাগবে। আর কম্পোনেন্ট বানালে আপনি শুধু মেইন যে কম্পোনেন্ট সে জায়গায় চেইঞ্জ করলে বাকি সব জায়গায় তা এমনিতেই চেইঞ্জ হয়ে যাবে। এই যে কোড রিইউজ বা ছোট ছোট কম্পোনেন্টে ভাগ করার যে কাজ সেই কনসেপ্ট থেকেই ফ্রন্টএন্ডের বর্তমান ট্রেন্ড চলছে। সেটাকে আরো কিভাবে ডেভেলপ করা যায় তার জন্য বিভিন্ন ডিজাইন প্যাটার্ন এসেছে যার মধ্যে একটা হলো আমাদের উল্লেখিত অ্যাটমিক ডিজাইন প্যাটার্ন। এই প্যাটার্নটা ৫টা জিনিসের উপর ভিত্তি করে গঠিত। সেগুলো হলো - + +- Atoms - Atom মানে হলো একটা অ্যাপ্লিকেশনের ছোট একটা জিনিস। সেটা একটা টেক্সট হতে পারে, একটা বাটন হতে পারে, একটা ইনপুট ফিল্ড হতে পারে। + +![atoms](./images/atoms.jpg) + +- Molecules - যখন একাধিক অ্যাটম যুক্ত হয় তখন তৈরি হয় একটা মলিকিউল। নিচের ছবিতে দেখুন একটা টেক্সট আছে, একটা ইনপুট ফিল্ড আছে আর একটা বাটন আছে। তিনটা অ্যাটম মিলে তৈরি হয়েছে একটা সার্চ কম্পোনেন্ট। এটাই মলিকিউল। + +![molecules](./images/molecule.jpg) + +- Organisms - অনেকগুলো মলিকিউল মিলে তৈরি হয়ে অর্গানিজম। যেমন একটা ন্যাভবার। ন্যাভবারের অনেক মলিকিউল থাকে। একটা লোগো থাকে, লিংক থাকে, ড্রপডাউন থাকে, প্রোফাইল পিক থাকে, সার্চ বার থাকে। এগুলো এক একটা মলিকিউল। এসব মলিকিউল মিলে তৈরি হচ্ছে ন্যাভবার। একে আমরা বলি অর্গানিজম। + +![Organisms](./images/organism-examples.jpg) + +- Templates - টেমপ্লেট মানে হলো আমার যেখানে যে যে কনটেন্ট থাকবে তার আসল কনটেন্ট ব্যবহার না করে কিছু প্লেসহোল্ডার রেখে দিয়ে বুঝাতে পারি যে এখানে ইমেজ হবে যার সাইজ হবে এতটুকু, এখানে প্যারাগ্রাফ হবে যার সাইজ এতটুকু। এই জায়গায় তিনটা সেকশন হবে যার দুইটা সেকশনে থাকবে লেখা আর মাঝখানে থাকবে ইমেজ। আমরা সব বুঝিয়ে দিবো কিন্তু কোনো কনটেন্ট ব্যবহার না করে। এটাকেই মূলত বলে টেমপ্লেইট। নিচের ছবিটি দেখলে আপনারা বুঝতে পারবেন। + +![Templates](./images/template1.jpg) + +- Pages - আমরা টেমপ্লেটের উপর যখন কনটেন্ট বসিয়ে ফেলব তখন যেটা দাঁড়াবে সেটা হলো একটা পেইজ। + +![Pages](./images/page1.jpg) + +এই যে অ্যাটমিক ডিজাইনের কনসেপ্ট, এই কনসেপ্টের উপর ভিত্তি করেই ম্যাক্সিমাম ডিজাইন সিস্টেম গড়ে উঠেছে। এটা এখন অনেক জনপ্রিয় একটা কনসেপ্ট ডেভেলপমেন্ট ফিল্ডে। + +আমরা একটু কোডে যায় এবার। আমরা index.html, app.js ফাইল ক্রিয়েট করি এবং স্ক্রিপ্ট ফাইলকে এইচটিএমএলের সাথে কানেক্ট করি। + +```html + + + + + + + + Modern App + + + +
+ + +``` + +```js +// app.js +window.onload = function () { + main(); +}; + +function main() {} +``` + +বর্তমান ফ্রন্টেন্ড ডেভেলপমেন্টে এইচটিএমএলের ফাইলটির কোনো কাজ নেই। সেটা আপনি ফ্রেমওয়ার্ক দিয়ে হোক বা আপনি যদি মনে করেন কোনো ফেমওয়ার্ক ব্যবহার না করেই ভ্যানিলা জাভাস্ক্রিপ্ট দিয়ে একটা ওয়েব অ্যাপ্লিকেশন বানাবেন, যখনই ওয়েব অ্যাপ্লিকেশন বানানোর কথা চলে আসবে তখনই আর আমাদের এইচটিএমএল ফাইলের কাজ নেই। এখন প্রশ্ন আসতে পারে যদি কাজই না থাকে তাহলে আমরা ইনস্পেক্ট করলে এইচটিএমএল কোডগুলো কোত্থেকে আসে? এই কোডগুলো জাভাস্ক্রিপ্ট ব্যবহার করে ডায়নামিক্যালি তৈরি করা যায়। ডমের এপিআই আছে। সেগুলো দিয়ে তৈরি করা যায়। এখন এই এপিআইগুলো আপনি ব্যবহার করতে পারেন অথবা আপনি যদি মনে করে রিয়্যাক্ট, ভিউ বা অ্যাঙ্গুলারের মতো ফ্রেমওয়ার্ক ব্যবহার করবেন তাহলে তারা আপনার জন্য এই এপিআইগুলো কল করে রাখবে এবং আপনাকে ফ্রন্টএন্ড ডেভেলপমেন্টের একটা ভিন্ন ফ্লেভার দিবে। পর্দার পিছনে সবাই আল্টিমেটলি ডমের এপিআই ব্যবহার করছে। ডমের এপিআই দিয়ে কিভাবে কাজ করা যায়? আমরা যদি আমাদের এইচটিএমএল ফাইলে লাইভ সার্ভার দিয়ে ব্রাউজারে ওপেন করি তাহলে একটা খালি স্ক্রিন দেখবো। + +![blank](./images/blank.png) + +কিন্তু আমি একটা h1 ইলেমেন্ট দেখতে চাই। সেটা করার জন্য আমরা আমাদের জাভাস্ক্রিপ্ট ফাইলটাতে কিছু লিখবো। + +```js +// app.js + +window.onload = function () { + main(); +}; + +function main() { + const h1 = document.createElement('h1'); + h1.innerText = 'Hello World'; + + document.body.appendChild(h1); +} +``` + +এবার যদি আমরা আমাদের ব্রাউজারে আউটপুট দেখি দেখবো Hello World লেখা দেখাবে। + +![h1](./images/h1.png) + +আমরা কিন্তু এইচটিএমএল ফাইলে হাতই দিইনি। আমরা জাভাস্ক্রিপ্ট দিয়েই ডায়নামিক্যালি কনটেন্ট তৈরি করতে পারছি। এবার আমরা একটা প্যারাগ্রাফ লিখবো। + +```js +// app.js + +window.onload = function () { + main(); +}; + +function main() { + const h1 = document.createElement('h1'); + h1.innerText = 'Hello World'; + + const p = document.createElement('p'); + p.innerText = `Officia duis anim ea ullamco proident et adipisicing nisi ad labore. Mollit id id anim velit laborum cupidatat aute veniam. Exercitation non elit magna elit aliquip dolore excepteur reprehenderit ea id dolor. Adipisicing pariatur amet mollit excepteur nulla dolore deserunt ipsum sit tempor magna dolore commodo mollit.`; + + document.body.appendChild(h1); + document.body.appendChild(p); +} +``` + +দেখবো আমাদের পেইজে একটা প্যারাগ্রাফ চলে এসেছে। + +![p](./images/p.png) + +এখন আমরা চাইছি h1 এবং p কে গ্রুপ করতে বা একটা div এর মধ্যে রাখতে। তার জন্য আমাদের একটা div নিতে হবে। + +```js +// app.js + +window.onload = function () { + main(); +}; + +function main() { + const div = document.createElement('div'); + + const h1 = document.createElement('h1'); + h1.innerText = 'Hello World'; + + const p = document.createElement('p'); + p.innerText = `Officia duis anim ea ullamco proident et adipisicing nisi ad labore. Mollit id id anim velit laborum cupidatat aute veniam. Exercitation non elit magna elit aliquip dolore excepteur reprehenderit ea id dolor. Adipisicing pariatur amet mollit excepteur nulla dolore deserunt ipsum sit tempor magna dolore commodo mollit.`; + + div.appendChild(h1); + div.appendChild(p); + + document.body.appendChild(div); +} +``` + +আমরা ব্রাউজারে কোনো চেইঞ্জ না দেখলেও যদি ইনস্পেক্ট করি তাহলে দেখবো একটা div ক্রিয়েট হয়েছে এবং তার মধ্যে h1 এবং p দুইটা ট্যাগই রয়েছে। + +![div](./images/div.png) + +আমরা এই কাজটা আরো ডায়নামিক্যালি করতে পারি। আমরা দুইটা ফাংশন বানাবো। একটা কন্টেইনারের জন্য আরেকটা টেক্সটের জন্য। + +```js +function Container(children) { + const div = document.createElement('div'); + children.forEach((child) => div.appendChild(child)); + + return div; +} + +function Text(tag, value) { + const text = document.createElement(tag); + text.innerText = value; + return text; +} +``` + +এবার আমরা মেইন ফাংশনের ভেতরে যা যা লিখেছি সব ডিলিট করে এই ফাংশনগুলো ব্যবহার করবো। + +```js +window.onload = function () { + main(); +}; + +function main() { + const app = Container([ + Text('h1', 'Hello World'), + Text('p', 'This is a simple paragraph'), + ]); + document.getElementById('root').appendChild(app); +} + +function Container(children) { + const div = document.createElement('div'); + children.forEach((child) => div.appendChild(child)); + + return div; +} + +function Text(tag, value) { + const text = document.createElement(tag); + text.innerText = value; + return text; +} +``` + +এবার যদি আমরা আউটপুট দেখি দেখবো আমাদের আগের মতো আউটপুট এসেছে। + +![dynamic](./images/dynamic.png) + +এটা অনেকটা রিয়্যাক্টের jsx আসার আগে যেভাবে লিখতে হতো সেরকম হয়েছে। এবার আপনি যতো কন্টেইনার বা টেক্সট নিতে চান আপনাকে আর কোড লিখতে হবে না প্রতিটার জন্য। এই ফাংশনগুলো ব্যবহার করেই আপনি ডায়নামিক্যালি পেইজে কনটেন্ট ক্রিয়েট করতে পারবেন। আমরা চাইলে স্টাইলও দিতে পারবো। + +```js +window.onload = function () { + main(); +}; + +function main() { + const app = Container([ + Text('h1', 'Hello World'), + Text('p', 'This is a simple paragraph'), + Container([Text('h3', 'WOW'), Text('h3', 'NICE')], { + display: 'flex', + gap: '2rem', + }), + ]); + document.getElementById('root').appendChild(app); +} + +function Container(children, style = {}) { + const div = document.createElement('div'); + Object.keys(style).map((key) => { + div.style[key] = style[key]; + }); + children.forEach((child) => div.appendChild(child)); + + return div; +} + +function Text(tag, value) { + const text = document.createElement(tag); + text.innerText = value; + return text; +} +``` + +![style](./images/style.png) + +ফ্রেমওয়ার্কগুলো কিভাবে কাজ করে তার একটা আইডিয়া আমরা পেয়েছি। আর কিভাবে এইচটিএমএলে হাত না দিয়ে আমরা জাভাস্ক্রিপ্ট ব্যবহার করে ডায়নামিক্যালি এইচটিএমএল কোড জেনারেট করতে পারি তাও দেখলাম। আমাদের যদি কনসেপ্ট জানা থাকে আমরা নিজেরাই ফ্রেমওয়ার্ক বানিয়ে ফেলতে পারি। + +আমরা আমাদের UI কে এইচটিএমএলের মধ্যে না রেখে একটা জাভাস্ক্রিপ্ট ফাংশনের মধ্যে নিয়ে এসেছি। আমাদের মাথায় নিয়ে রাখতে হবে যে UI এখন আর এইচটিএমএলে নেই এটা এখন জাভাস্ক্রিপ্ট ফাংশনের মধ্যে চলে এসেছে। + +এবার আমরা যে কাজ করেছি তা রিয়্যাক্টে গিয়ে করবো। প্রথমে আমরা আমাদের রিয়্যাক্ট অ্যাপ্লিকেশন বানাই। তার জন্য কমান্ড লাইনে লিখতে হবে `npx create-react-app first-app` বা `yarn create react-app first-app`। + +আমাদের এখন ফাইল স্ট্রাকচার বুঝার দরকার নেই। আমরা শুধু দেখবো যে কোড আমরা আগে করেছিলাম ওটার সাথে রিয়্যাক্টের মিল আছে কিনা। + +আমরা src ফোল্ডারের মধ্যে App.js এর সবকিছু ডিলিট করে দিয়ে নিচের কোডটি লিখবো। + +```js +// App.js + +import React from 'react'; + +function App() { + return React.createElement('div', null, [ + React.createElement('h1', null, 'Hello React'), + React.createElement('p', null, 'React is all about JavaScript'), + ]); +} + +export default App; +``` + +এবার আমরা `yarn start` বা `npm start` লিখে আমাদের অ্যাপ্লিকেশন চালু করবো। এটা ব্রাউজারে অটোমেটিক রান হয়ে যাবে। সেখানে গেলে দেখবো আমরা নিচের আউটপুট। + +![react-1](./images/react-1.png) + +আমরা আগে ম্যানুয়েলি যে কোড লিখেছিলাম প্রায় একই। ওখানে document.createElement লিখেছিলাম এখানে React.createElement। তার মানে আমরা যে jsx কোড লিখি তার কোনো ভিত্তিই নেই। সেগুলো আমরা লেখার সুবিধার্থে সেই সিস্টেম করা হয়েছে। কিন্তু সেই jsx কোডই কোনো না কোনো ভাবে ফাংশনে রূপান্তরিত হচ্ছে। এখন এটা ছোট অ্যাপ্লিকেশন বলে এভাবে লিখলেও সমস্যা হচ্ছে না। কিন্তু বড় অ্যাপ্লিকেশন যেখানে হাজার হাজার কোড লিখতে হয় সেখানে এভাবে লিখলে বুঝা যায় না। তাই রিয়্যাক্ট jsx ফিচার এনেছে কোড সহজে লেখার জন্য, বুঝার জন্য এবং ম্যানেজ করার জন্য। এবার আমরা যদি উপরের কোডটা মডার্ন রিয়্যাক্টে লিখি তাহলে দেখুন কতো সহজে এইচটিএমএলের মতো করে লিখতে পারি। + +```js +function App() { + return ( +
+

Hello React

+

React is really awesome

+
+ ); +} + +export default App; +``` + +![react-2](./images/react-2.png) + +দেখুন কতো সহজেই আমরা লিখতে পারছি। যদিও এই কোডটা ব্রাউজারে দেখাবে না। সে আগে জাভাস্ক্রিপ্টে কনভার্ট হবে এরপর ব্রাউজারে শো করবে। সেই কাজটা আমাদের করতে হবে না। সেটা আমাদের হয়ে রিয়্যাক্ট, ওয়েবপ্যাক, ব্যাভেল করবে। আমাদের সেটা নিয়ে ভাবারও দরকার নাই। + +আমরা যেটা বলেছিলাম এখন UI মানে ফাংশন সেটা প্রমাণিত হলো। আমরা আরেকটা ফাংশন বানাই এই ফাইলে। আমরা একটা বাটন যুক্ত করতে চাইছি। সেই বাটনের জন্য একটা ফাংশন লিখে ফেলি। + +```js +function Button() { + return ; +} + +function App() { + return ( +
+

Hello React

+

React is really awesome

+
+ ); +} + +export default App; +``` + +আমরা লিখবো ফাংশন। কিন্তু সেটাকে আমরা আমাদের কোডে লিখবো ট্যাগ আকারে। এবার আউটপুট দেখলে দেখা যাবে একটা বাটন যুক্ত হয়ে গেছে। + +![react-3](./images/react-3.png) + +তার মানে এখানে যতটা UI ততোটা ফাংশন তৈরি করবো। ছোট ছোট UI এর সমন্বয়ে আমরা একটা বড় UI তৈরি করবো। যেমন আমরা চাইলে h1 এবং p ট্যাগের জন্যও ফাংশন তৈরি করতে পারি। + +```js +function Button() { + return ; +} + +function Title() { + return

Hello React

; +} + +function Body() { + return

React is really awesome

; +} + +function App() { + return ( +
+ + <Body /> + <Button /> + </div> + ); +} + +export default App; +``` + +![react-4](./images/react-4.png) + +দেখুন সেইম আউটপুট আসছে। তার মানে আশা করি এখন আর কারো মনে সন্দেহ নেই যে একেকটা UI একেকটা ফাংশন। + +Why React? এর উত্তর আমরা পেয়ে গেছি। আমরা ডম ম্যানিপুলেশন করে যে প্রজেক্ট করেছিলাম সেটার কোড কতো কমপ্লেক্স আর এখানে কতো সহজ। ডম ম্যানিপুলেশন করে করতে গেলে যে যে সমস্যার সম্মুখীন আমাদের হতে হতো সে সমস্যা থেকে মুক্তির জন্য রিয়্যাক্ট বেস্ট সল্যুশন। তাহলে রিয়্যাক্ট কেন সেই উত্তর আমরা পেলাম। + +এবার আমাদের জানতে হবে রিয়্যাক্ট কি, কিভাবে আমরা রিয়্যাক্টে কাজ করবো, রিয়্যাক্ট নিয়ে কাজ করতে গেলে আমাদের পিসিতে কি কি টুলস লাগবে সেগুলো। + +প্রথমে আমরা জানি রিয়্যাক্ট কি? রিয়্যাক্টের অফিসিয়াল [সাইট](https://reactjs.org/) এ গেলে দেখবো সেখানে লেখা আছে `A JavaScript library for building user interfaces`। অ্যাঙ্গুলারের [সাইটে](https://angular.io/) গেলে দেখা যাবে সেটা ফ্রেমওয়ার্ক, ভিউ এর [সাইটে](https://vuejs.org/) গেলে দেখা যাবে সেটাও ফ্রেমওয়ার্ক। কিন্তু রিয়্যাক্টকে বলা হচ্ছে লাইব্রেরি। তাহলে লাইব্রেরি আর ফ্রেমওয়ার্কের মধ্যে পার্থক্য কি? বেসিক পার্থক্য হলো আমরা লাইব্রেরিকে কন্ট্রোল করতে পারি, নিজের মতো যেমন খুশি সেভাবে ব্যবহার করতে পারি। কিন্তু ফ্রেমওয়ার্ক আমাদের কন্ট্রোল করে। নিজের মতো করে কিছু আমরা ফ্রেমওয়ার্কে করতে পারি না। এরপর ফ্রেমওয়ার্কে ওদের গন্ডির বাইরে আমরা কোনো প্যাকেজ ব্যবহার করতে পারবো না। ওখানে যে যে কাজের জন্য যে যে প্যাকেজ বা লাইব্রেরির কথা বলা হয়েছে তার বাইরে আমরা যেতে পারি না। কিন্তু লাইব্রেরির ক্ষেত্রে আমরা স্বাধীন। আমরা যদি দেখি গতানুগতিক লাইব্রেরির বাইরে ভাল একটা লাইব্রেরি বা প্যাকেজ আছে সেটা আমরা ব্যবহার করতে পারি। ফ্রেমওয়ার্কের ক্ষেত্রে আরেকটা অসুবিধা হলো যেহেতু তাদের সবকিছুই বিল্টইন ভাবে আছে সেহেতু আমাদের সবকিছুই শিখতে হয় অর্থাৎ এর লার্নিং কার্ভ অনেক বড়। লাইব্রেরির ক্ষেত্রে লার্নিং কার্ভ তুলনামূলক অনেক ছোট। + +রিয়্যাক্ট নিয়ে কাজ করতে গেলে আমাদের তিনটা টুলসের দরকার হয়। + +- [NodeJS](https://nodejs.org/en/) - এটা মাস্ট ইনস্টল থাকা লাগবেই। +- [Babel Js](https://babeljs.io/) - এটা যদি কাস্টমভাবে আমরা রিয়্যাক্টের সেটআপ তৈরি করতে চাই এটা আমাদের ইনস্টল করতেই হবে। কিন্তু আমরা কাস্টমভাবে রিয়্যাক্টের সেটআপ তৈরি করবো না। +- [Webpack](https://webpack.js.org/) - আমরা আমাদের প্রজেক্টে অসংখ্য ফাইল নিয়ে কাজ করবো। সেই অসংখ্য ফাইলকে বান্ডেল আকারে ছোট ফাইলে একত্রীভূত করার জন্য আমাদের এটা লাগে। যদি কাস্টমভাবে আমরা রিয়্যাক্টের এনভায়রনমেন্ট তৈরি করতে চাই তাহলে আমাদের ওয়েবপ্যাক এবং ব্যাবেল লাগবেই। + +আমরা বিগিনার হিসেবে কাস্টমভাবে রিয়্যাক্টের এনভায়রনমেন্ট সেটআপ করবো না। তার জন্য যেভাবে আমরা আমাদের রিয়্যাক্ট অ্যাপ তৈরি করেছিলাম সেভাবেই তৈরি করবো। ঐ create-react-app টুলই আমাদের জন্য বিহাইন্ড দ্য সীণ ব্যাবেল এবং ওয়েবপ্যাক ব্যবহার করে একটা এনভায়রনমেন্ট তৈরি করে দিবে। + +এবার আমরা একটু রিয়্যাক্টের ফাইল স্ট্রাকচার দেখি। এখানে node_modules এ আমাদের প্রয়োজনীয় প্যাকেজগুলো ইনস্টল হয়ে আছে। এরপর আছে public ডিরেক্টরি। সেখানে আমরা favicon রাখতে পারি। index.html ফাইল রাখবো যেটা একমাত্র এইচটিএমএল ফাইল। এরপর কিছু লোগো আছে। এরপর আছে manifest.json। এটা আমাদের কাজে লাগবে যখন আমরা প্রোগ্রেসিভ ওয়েব অ্যাপ তৈরি করবো বা ক্রোম এক্সটেনশন তৈরি করবো তখন লাগবে। এরপর আছে robots.txt। এগুলো বিগিনার হিসেবে দরকার নেই এখন। এরপর যাবো src ডিরেক্টরিতে। এখানে App.js আমাদের রুট ফাইল। যদিও এটা আমাদের এন্ট্রি ফাইল না। আমাদের এন্ট্রি ফাইল হলো index.js ফাইল। এটাই সেই ফাইল যেখানে আমরা বুট করি। এই ফাইলে গেলে আমরা দেখবো এটার চেহারা এমন। + +```js +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import './index.css'; +import App from './App'; +import reportWebVitals from './reportWebVitals'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + <React.StrictMode> + <App /> + </React.StrictMode> +); + +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +reportWebVitals(); +``` + +আমরা এখান থেকে React.StrictMode মুছে দিবো। কারণ বিগিনার হিসেবে আমরা স্ট্রিক্টমুড নিয়ে কাজ করবো না। কারণ আমরা জানিই না স্ট্রিক্টমুড কি কি হতে পারে। সুতরাং এটাকে আমরা একটু রিফ্র্যাক্টর করে নিবো এভাবে। + +```js +import ReactDOM from 'react-dom/client'; +import App from './App'; +import './index.css'; +import reportWebVitals from './reportWebVitals'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render(<App />); + +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +reportWebVitals(); +``` + +App.js ডিলিট করে আমরা বানাবো App.jsx ফাইল। .jsx মানে হলো আমরা বুঝতে পারবো এই ফাইলে আমরা কম্পোনেন্ট নিয়ে কাজ করেছি আর .js মানে হলো এখানে শুধু আমরা পিওর জাভাস্ক্রিপ্ট কোড লিখেছি। আরো একটা সুবিধা হচ্ছে আমরা এইচটিএমএলের সমস্ত শর্টহ্যান্ড সুবিধা এখানে পাবো। যেমন .something লিখে এন্টার দিলে যেরকম ক্লাসনেম সহ div তৈরি হয় সেসকল সুবিধা। আরেকটা জিনিস খেয়াল রাখতে হবে আমাদের কাস্টম কম্পোনেন্ট ফাইলের নাম সবসময় ক্যাপিটাল লেটার দিয়ে শুরু হতে হবে। এখন কম্পোনেটের সুবিধা কি। আমরা আমাদের App.jsx এ আগে যে কোড লিখেছিলাম ওখানে যদি ৪টা বাটন থাকতো এবং সেগুলো স্টাইল করা থাকতো এবং সেগুলো যদি কম্পোনেন্ট আকারে না থেকে কপি পেস্ট করে ৪বার করে লিখতাম, তাহলে যদি কখনও কোনো স্টাইল চেইঞ্জ করতে হতো তাহলে ৪টাতেই খুঁজে খুঁজে চেইঞ্জ করতে হতো। কম্পোনেন্ট করার কারণে আমরা শুধু ঐ একটা কম্পোনেন্ট ফাংশনে চেইঞ্জ করবো বাকি জায়গায় এমনিই চেইঞ্জ হয়ে যাবে। এবার যদি আমরা চাই একেকটা বাটনের নাম একেকটা হবে সেটাও ডায়নামিক্যালি আমরা করতে পারি। + +```js +function Button({ text }) { + return <button style={{ marginRight: '1rem' }}>{text}</button>; +} + +function Title() { + return <h1>Hello React</h1>; +} + +function Body() { + return <p>React is really awesome</p>; +} + +function App() { + return ( + <div> + <Title /> + <Body /> + <Button text="Button A" /> + <Button text="Button B" /> + <Button text="Button C" /> + </div> + ); +} + +export default App; +``` + +![react-5](./images/react-5.png) + +দেখুন আমরা কত সহজে তা করতে পারছি। তার মানে এখন ছোট ছোট কম্পোনেন্ট করে কেন কাজ করা হয় আশা করি ধারণা পেয়ে গেছেন আপনারা। এটাই মডার্ণ ওয়েব ডেভেলপমেন্ট। + +## Acknowledgement + +All images related to atomic design collected from [Atomic Design](https://bradfrost.com/blog/post/atomic-web-design/). + +## Source Code + +- [Source Code for this lecture](../../src/lecture-29/) + +## Author + +- [Aditya Chakraborty](https://github.com/adityackr) diff --git a/full-stack-army/class-overview/Lecture-29/images/atoms.jpg b/full-stack-army/class-overview/Lecture-29/images/atoms.jpg new file mode 100644 index 0000000..f681255 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-29/images/atoms.jpg differ diff --git a/full-stack-army/class-overview/Lecture-29/images/blank.png b/full-stack-army/class-overview/Lecture-29/images/blank.png new file mode 100644 index 0000000..7ab655d Binary files /dev/null and b/full-stack-army/class-overview/Lecture-29/images/blank.png differ diff --git a/full-stack-army/class-overview/Lecture-29/images/div.png b/full-stack-army/class-overview/Lecture-29/images/div.png new file mode 100644 index 0000000..83ea497 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-29/images/div.png differ diff --git a/full-stack-army/class-overview/Lecture-29/images/dynamic.png b/full-stack-army/class-overview/Lecture-29/images/dynamic.png new file mode 100644 index 0000000..8fdd2d7 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-29/images/dynamic.png differ diff --git a/full-stack-army/class-overview/Lecture-29/images/h1.png b/full-stack-army/class-overview/Lecture-29/images/h1.png new file mode 100644 index 0000000..70e6720 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-29/images/h1.png differ diff --git a/full-stack-army/class-overview/Lecture-29/images/molecule.jpg b/full-stack-army/class-overview/Lecture-29/images/molecule.jpg new file mode 100644 index 0000000..fd9b957 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-29/images/molecule.jpg differ diff --git a/full-stack-army/class-overview/Lecture-29/images/organism-examples.jpg b/full-stack-army/class-overview/Lecture-29/images/organism-examples.jpg new file mode 100644 index 0000000..71795e8 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-29/images/organism-examples.jpg differ diff --git a/full-stack-army/class-overview/Lecture-29/images/p.png b/full-stack-army/class-overview/Lecture-29/images/p.png new file mode 100644 index 0000000..84d182e Binary files /dev/null and b/full-stack-army/class-overview/Lecture-29/images/p.png differ diff --git a/full-stack-army/class-overview/Lecture-29/images/page1.jpg b/full-stack-army/class-overview/Lecture-29/images/page1.jpg new file mode 100644 index 0000000..4dd011f Binary files /dev/null and b/full-stack-army/class-overview/Lecture-29/images/page1.jpg differ diff --git a/full-stack-army/class-overview/Lecture-29/images/product_hunt.png b/full-stack-army/class-overview/Lecture-29/images/product_hunt.png new file mode 100644 index 0000000..bd5a447 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-29/images/product_hunt.png differ diff --git a/full-stack-army/class-overview/Lecture-29/images/react-1.png b/full-stack-army/class-overview/Lecture-29/images/react-1.png new file mode 100644 index 0000000..e915303 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-29/images/react-1.png differ diff --git a/full-stack-army/class-overview/Lecture-29/images/react-2.png b/full-stack-army/class-overview/Lecture-29/images/react-2.png new file mode 100644 index 0000000..b9b7942 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-29/images/react-2.png differ diff --git a/full-stack-army/class-overview/Lecture-29/images/react-3.png b/full-stack-army/class-overview/Lecture-29/images/react-3.png new file mode 100644 index 0000000..8d21d67 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-29/images/react-3.png differ diff --git a/full-stack-army/class-overview/Lecture-29/images/react-4.png b/full-stack-army/class-overview/Lecture-29/images/react-4.png new file mode 100644 index 0000000..9d58e0f Binary files /dev/null and b/full-stack-army/class-overview/Lecture-29/images/react-4.png differ diff --git a/full-stack-army/class-overview/Lecture-29/images/react-5.png b/full-stack-army/class-overview/Lecture-29/images/react-5.png new file mode 100644 index 0000000..1a17405 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-29/images/react-5.png differ diff --git a/full-stack-army/class-overview/Lecture-29/images/style.png b/full-stack-army/class-overview/Lecture-29/images/style.png new file mode 100644 index 0000000..f65e23b Binary files /dev/null and b/full-stack-army/class-overview/Lecture-29/images/style.png differ diff --git a/full-stack-army/class-overview/Lecture-29/images/template1.jpg b/full-stack-army/class-overview/Lecture-29/images/template1.jpg new file mode 100644 index 0000000..4421df2 Binary files /dev/null and b/full-stack-army/class-overview/Lecture-29/images/template1.jpg differ diff --git a/class-overview/README.md b/full-stack-army/class-overview/README.md similarity index 100% rename from class-overview/README.md rename to full-stack-army/class-overview/README.md diff --git a/demo/react-demo/.gitignore b/full-stack-army/demo/react-demo/.gitignore similarity index 100% rename from demo/react-demo/.gitignore rename to full-stack-army/demo/react-demo/.gitignore diff --git a/demo/react-demo/index.html b/full-stack-army/demo/react-demo/index.html similarity index 100% rename from demo/react-demo/index.html rename to full-stack-army/demo/react-demo/index.html diff --git a/demo/react-demo/package.json b/full-stack-army/demo/react-demo/package.json similarity index 100% rename from demo/react-demo/package.json rename to full-stack-army/demo/react-demo/package.json diff --git a/demo/react-demo/src/App.css b/full-stack-army/demo/react-demo/src/App.css similarity index 100% rename from demo/react-demo/src/App.css rename to full-stack-army/demo/react-demo/src/App.css diff --git a/demo/react-demo/src/App.jsx b/full-stack-army/demo/react-demo/src/App.jsx similarity index 100% rename from demo/react-demo/src/App.jsx rename to full-stack-army/demo/react-demo/src/App.jsx diff --git a/demo/react-demo/src/App10.jsx b/full-stack-army/demo/react-demo/src/App10.jsx similarity index 100% rename from demo/react-demo/src/App10.jsx rename to full-stack-army/demo/react-demo/src/App10.jsx diff --git a/demo/react-demo/src/App2.jsx b/full-stack-army/demo/react-demo/src/App2.jsx similarity index 100% rename from demo/react-demo/src/App2.jsx rename to full-stack-army/demo/react-demo/src/App2.jsx diff --git a/demo/react-demo/src/App3.jsx b/full-stack-army/demo/react-demo/src/App3.jsx similarity index 100% rename from demo/react-demo/src/App3.jsx rename to full-stack-army/demo/react-demo/src/App3.jsx diff --git a/demo/react-demo/src/App4.jsx b/full-stack-army/demo/react-demo/src/App4.jsx similarity index 100% rename from demo/react-demo/src/App4.jsx rename to full-stack-army/demo/react-demo/src/App4.jsx diff --git a/demo/react-demo/src/App5.jsx b/full-stack-army/demo/react-demo/src/App5.jsx similarity index 100% rename from demo/react-demo/src/App5.jsx rename to full-stack-army/demo/react-demo/src/App5.jsx diff --git a/demo/react-demo/src/App6.jsx b/full-stack-army/demo/react-demo/src/App6.jsx similarity index 100% rename from demo/react-demo/src/App6.jsx rename to full-stack-army/demo/react-demo/src/App6.jsx diff --git a/demo/react-demo/src/App7.jsx b/full-stack-army/demo/react-demo/src/App7.jsx similarity index 100% rename from demo/react-demo/src/App7.jsx rename to full-stack-army/demo/react-demo/src/App7.jsx diff --git a/demo/react-demo/src/App8.jsx b/full-stack-army/demo/react-demo/src/App8.jsx similarity index 100% rename from demo/react-demo/src/App8.jsx rename to full-stack-army/demo/react-demo/src/App8.jsx diff --git a/demo/react-demo/src/App9.jsx b/full-stack-army/demo/react-demo/src/App9.jsx similarity index 100% rename from demo/react-demo/src/App9.jsx rename to full-stack-army/demo/react-demo/src/App9.jsx diff --git a/demo/react-demo/src/App_hook.js b/full-stack-army/demo/react-demo/src/App_hook.js similarity index 100% rename from demo/react-demo/src/App_hook.js rename to full-stack-army/demo/react-demo/src/App_hook.js diff --git a/demo/react-demo/src/components/contact-app/ContactForm.jsx b/full-stack-army/demo/react-demo/src/components/contact-app/ContactForm.jsx similarity index 100% rename from demo/react-demo/src/components/contact-app/ContactForm.jsx rename to full-stack-army/demo/react-demo/src/components/contact-app/ContactForm.jsx diff --git a/demo/react-demo/src/components/contact-app/Table.jsx b/full-stack-army/demo/react-demo/src/components/contact-app/Table.jsx similarity index 100% rename from demo/react-demo/src/components/contact-app/Table.jsx rename to full-stack-army/demo/react-demo/src/components/contact-app/Table.jsx diff --git a/demo/react-demo/src/components/history/HistoryItem.jsx b/full-stack-army/demo/react-demo/src/components/history/HistoryItem.jsx similarity index 100% rename from demo/react-demo/src/components/history/HistoryItem.jsx rename to full-stack-army/demo/react-demo/src/components/history/HistoryItem.jsx diff --git a/demo/react-demo/src/components/history/HistorySection.jsx b/full-stack-army/demo/react-demo/src/components/history/HistorySection.jsx similarity index 100% rename from demo/react-demo/src/components/history/HistorySection.jsx rename to full-stack-army/demo/react-demo/src/components/history/HistorySection.jsx diff --git a/demo/react-demo/src/components/inputs/InputSection.jsx b/full-stack-army/demo/react-demo/src/components/inputs/InputSection.jsx similarity index 100% rename from demo/react-demo/src/components/inputs/InputSection.jsx rename to full-stack-army/demo/react-demo/src/components/inputs/InputSection.jsx diff --git a/demo/react-demo/src/components/operations/OperationSection.jsx b/full-stack-army/demo/react-demo/src/components/operations/OperationSection.jsx similarity index 100% rename from demo/react-demo/src/components/operations/OperationSection.jsx rename to full-stack-army/demo/react-demo/src/components/operations/OperationSection.jsx diff --git a/demo/react-demo/src/components/ui/Button.jsx b/full-stack-army/demo/react-demo/src/components/ui/Button.jsx similarity index 100% rename from demo/react-demo/src/components/ui/Button.jsx rename to full-stack-army/demo/react-demo/src/components/ui/Button.jsx diff --git a/demo/react-demo/src/components/ui/NumberField.jsx b/full-stack-army/demo/react-demo/src/components/ui/NumberField.jsx similarity index 100% rename from demo/react-demo/src/components/ui/NumberField.jsx rename to full-stack-army/demo/react-demo/src/components/ui/NumberField.jsx diff --git a/demo/react-demo/src/hooks/useCounter.js b/full-stack-army/demo/react-demo/src/hooks/useCounter.js similarity index 100% rename from demo/react-demo/src/hooks/useCounter.js rename to full-stack-army/demo/react-demo/src/hooks/useCounter.js diff --git a/demo/react-demo/src/hooks/useFetchData.js b/full-stack-army/demo/react-demo/src/hooks/useFetchData.js similarity index 100% rename from demo/react-demo/src/hooks/useFetchData.js rename to full-stack-army/demo/react-demo/src/hooks/useFetchData.js diff --git a/demo/react-demo/src/main.jsx b/full-stack-army/demo/react-demo/src/main.jsx similarity index 100% rename from demo/react-demo/src/main.jsx rename to full-stack-army/demo/react-demo/src/main.jsx diff --git a/demo/react-demo/vite.config.js b/full-stack-army/demo/react-demo/vite.config.js similarity index 100% rename from demo/react-demo/vite.config.js rename to full-stack-army/demo/react-demo/vite.config.js diff --git a/demo/react-demo/yarn.lock b/full-stack-army/demo/react-demo/yarn.lock similarity index 100% rename from demo/react-demo/yarn.lock rename to full-stack-army/demo/react-demo/yarn.lock diff --git a/demo/react-structure/.gitignore b/full-stack-army/demo/react-structure/.gitignore similarity index 100% rename from demo/react-structure/.gitignore rename to full-stack-army/demo/react-structure/.gitignore diff --git a/demo/react-structure/index.html b/full-stack-army/demo/react-structure/index.html similarity index 100% rename from demo/react-structure/index.html rename to full-stack-army/demo/react-structure/index.html diff --git a/demo/react-structure/package.json b/full-stack-army/demo/react-structure/package.json similarity index 100% rename from demo/react-structure/package.json rename to full-stack-army/demo/react-structure/package.json diff --git a/demo/react-structure/src/app/App.jsx b/full-stack-army/demo/react-structure/src/app/App.jsx similarity index 100% rename from demo/react-structure/src/app/App.jsx rename to full-stack-army/demo/react-structure/src/app/App.jsx diff --git a/demo/react-structure/src/app/App2.jsx b/full-stack-army/demo/react-structure/src/app/App2.jsx similarity index 100% rename from demo/react-structure/src/app/App2.jsx rename to full-stack-army/demo/react-structure/src/app/App2.jsx diff --git a/demo/react-structure/src/app/App3.jsx b/full-stack-army/demo/react-structure/src/app/App3.jsx similarity index 100% rename from demo/react-structure/src/app/App3.jsx rename to full-stack-army/demo/react-structure/src/app/App3.jsx diff --git a/demo/react-structure/src/components/UI/buttons/Button.jsx b/full-stack-army/demo/react-structure/src/components/UI/buttons/Button.jsx similarity index 100% rename from demo/react-structure/src/components/UI/buttons/Button.jsx rename to full-stack-army/demo/react-structure/src/components/UI/buttons/Button.jsx diff --git a/demo/react-structure/src/components/UI/inputs/Label.jsx b/full-stack-army/demo/react-structure/src/components/UI/inputs/Label.jsx similarity index 100% rename from demo/react-structure/src/components/UI/inputs/Label.jsx rename to full-stack-army/demo/react-structure/src/components/UI/inputs/Label.jsx diff --git a/demo/react-structure/src/components/UI/inputs/TextInput.jsx b/full-stack-army/demo/react-structure/src/components/UI/inputs/TextInput.jsx similarity index 100% rename from demo/react-structure/src/components/UI/inputs/TextInput.jsx rename to full-stack-army/demo/react-structure/src/components/UI/inputs/TextInput.jsx diff --git a/demo/react-structure/src/components/UI/texts/Text.jsx b/full-stack-army/demo/react-structure/src/components/UI/texts/Text.jsx similarity index 100% rename from demo/react-structure/src/components/UI/texts/Text.jsx rename to full-stack-army/demo/react-structure/src/components/UI/texts/Text.jsx diff --git a/demo/react-structure/src/components/shared/forms/InputGroup.jsx b/full-stack-army/demo/react-structure/src/components/shared/forms/InputGroup.jsx similarity index 100% rename from demo/react-structure/src/components/shared/forms/InputGroup.jsx rename to full-stack-army/demo/react-structure/src/components/shared/forms/InputGroup.jsx diff --git a/demo/react-structure/src/components/task/Task.jsx b/full-stack-army/demo/react-structure/src/components/task/Task.jsx similarity index 100% rename from demo/react-structure/src/components/task/Task.jsx rename to full-stack-army/demo/react-structure/src/components/task/Task.jsx diff --git a/demo/react-structure/src/hooks/useForm.js b/full-stack-army/demo/react-structure/src/hooks/useForm.js similarity index 100% rename from demo/react-structure/src/hooks/useForm.js rename to full-stack-army/demo/react-structure/src/hooks/useForm.js diff --git a/demo/react-structure/src/main.css b/full-stack-army/demo/react-structure/src/main.css similarity index 100% rename from demo/react-structure/src/main.css rename to full-stack-army/demo/react-structure/src/main.css diff --git a/demo/react-structure/src/main.jsx b/full-stack-army/demo/react-structure/src/main.jsx similarity index 100% rename from demo/react-structure/src/main.jsx rename to full-stack-army/demo/react-structure/src/main.jsx diff --git a/demo/react-structure/src/pages/dashboard/index.jsx b/full-stack-army/demo/react-structure/src/pages/dashboard/index.jsx similarity index 100% rename from demo/react-structure/src/pages/dashboard/index.jsx rename to full-stack-army/demo/react-structure/src/pages/dashboard/index.jsx diff --git a/demo/react-structure/src/utils/object-utils.js b/full-stack-army/demo/react-structure/src/utils/object-utils.js similarity index 100% rename from demo/react-structure/src/utils/object-utils.js rename to full-stack-army/demo/react-structure/src/utils/object-utils.js diff --git a/demo/react-structure/vite.config.js b/full-stack-army/demo/react-structure/vite.config.js similarity index 100% rename from demo/react-structure/vite.config.js rename to full-stack-army/demo/react-structure/vite.config.js diff --git a/demo/react-structure/yarn.lock b/full-stack-army/demo/react-structure/yarn.lock similarity index 100% rename from demo/react-structure/yarn.lock rename to full-stack-army/demo/react-structure/yarn.lock diff --git a/demo/track-zone/.gitignore b/full-stack-army/demo/track-zone/.gitignore similarity index 100% rename from demo/track-zone/.gitignore rename to full-stack-army/demo/track-zone/.gitignore diff --git a/demo/track-zone/index.html b/full-stack-army/demo/track-zone/index.html similarity index 100% rename from demo/track-zone/index.html rename to full-stack-army/demo/track-zone/index.html diff --git a/demo/track-zone/package.json b/full-stack-army/demo/track-zone/package.json similarity index 100% rename from demo/track-zone/package.json rename to full-stack-army/demo/track-zone/package.json diff --git a/demo/track-zone/src/App.jsx b/full-stack-army/demo/track-zone/src/App.jsx similarity index 100% rename from demo/track-zone/src/App.jsx rename to full-stack-army/demo/track-zone/src/App.jsx diff --git a/demo/track-zone/src/components/clock-list/clock-list-item.jsx b/full-stack-army/demo/track-zone/src/components/clock-list/clock-list-item.jsx similarity index 100% rename from demo/track-zone/src/components/clock-list/clock-list-item.jsx rename to full-stack-army/demo/track-zone/src/components/clock-list/clock-list-item.jsx diff --git a/demo/track-zone/src/components/clock-list/index.jsx b/full-stack-army/demo/track-zone/src/components/clock-list/index.jsx similarity index 100% rename from demo/track-zone/src/components/clock-list/index.jsx rename to full-stack-army/demo/track-zone/src/components/clock-list/index.jsx diff --git a/demo/track-zone/src/components/local-clock/index.jsx b/full-stack-army/demo/track-zone/src/components/local-clock/index.jsx similarity index 100% rename from demo/track-zone/src/components/local-clock/index.jsx rename to full-stack-army/demo/track-zone/src/components/local-clock/index.jsx diff --git a/demo/track-zone/src/components/shared/clock-actions/index.jsx b/full-stack-army/demo/track-zone/src/components/shared/clock-actions/index.jsx similarity index 100% rename from demo/track-zone/src/components/shared/clock-actions/index.jsx rename to full-stack-army/demo/track-zone/src/components/shared/clock-actions/index.jsx diff --git a/demo/track-zone/src/components/shared/clock-display/index.jsx b/full-stack-army/demo/track-zone/src/components/shared/clock-display/index.jsx similarity index 100% rename from demo/track-zone/src/components/shared/clock-display/index.jsx rename to full-stack-army/demo/track-zone/src/components/shared/clock-display/index.jsx diff --git a/demo/track-zone/src/components/shared/clock-display/index.module.css b/full-stack-army/demo/track-zone/src/components/shared/clock-display/index.module.css similarity index 100% rename from demo/track-zone/src/components/shared/clock-display/index.module.css rename to full-stack-army/demo/track-zone/src/components/shared/clock-display/index.module.css diff --git a/demo/track-zone/src/components/shared/clock-form/index.jsx b/full-stack-army/demo/track-zone/src/components/shared/clock-form/index.jsx similarity index 100% rename from demo/track-zone/src/components/shared/clock-form/index.jsx rename to full-stack-army/demo/track-zone/src/components/shared/clock-form/index.jsx diff --git a/demo/track-zone/src/constants/timezone.js b/full-stack-army/demo/track-zone/src/constants/timezone.js similarity index 100% rename from demo/track-zone/src/constants/timezone.js rename to full-stack-army/demo/track-zone/src/constants/timezone.js diff --git a/demo/track-zone/src/hooks/useClock.jsx b/full-stack-army/demo/track-zone/src/hooks/useClock.jsx similarity index 100% rename from demo/track-zone/src/hooks/useClock.jsx rename to full-stack-army/demo/track-zone/src/hooks/useClock.jsx diff --git a/demo/track-zone/src/index.css b/full-stack-army/demo/track-zone/src/index.css similarity index 100% rename from demo/track-zone/src/index.css rename to full-stack-army/demo/track-zone/src/index.css diff --git a/demo/track-zone/src/main.jsx b/full-stack-army/demo/track-zone/src/main.jsx similarity index 100% rename from demo/track-zone/src/main.jsx rename to full-stack-army/demo/track-zone/src/main.jsx diff --git a/demo/track-zone/src/utils/timezone.js b/full-stack-army/demo/track-zone/src/utils/timezone.js similarity index 100% rename from demo/track-zone/src/utils/timezone.js rename to full-stack-army/demo/track-zone/src/utils/timezone.js diff --git a/demo/track-zone/vite.config.js b/full-stack-army/demo/track-zone/vite.config.js similarity index 100% rename from demo/track-zone/vite.config.js rename to full-stack-army/demo/track-zone/vite.config.js diff --git a/demo/track-zone/yarn.lock b/full-stack-army/demo/track-zone/yarn.lock similarity index 100% rename from demo/track-zone/yarn.lock rename to full-stack-army/demo/track-zone/yarn.lock diff --git a/examples/README.md b/full-stack-army/examples/README.md similarity index 100% rename from examples/README.md rename to full-stack-army/examples/README.md diff --git a/examples/icon-box/REDME.md b/full-stack-army/examples/icon-box/REDME.md similarity index 100% rename from examples/icon-box/REDME.md rename to full-stack-army/examples/icon-box/REDME.md diff --git a/examples/icon-box/icon-box-output.png b/full-stack-army/examples/icon-box/icon-box-output.png similarity index 100% rename from examples/icon-box/icon-box-output.png rename to full-stack-army/examples/icon-box/icon-box-output.png diff --git a/examples/icon-box/index.html b/full-stack-army/examples/icon-box/index.html similarity index 100% rename from examples/icon-box/index.html rename to full-stack-army/examples/icon-box/index.html diff --git a/examples/icon-box/style.css b/full-stack-army/examples/icon-box/style.css similarity index 100% rename from examples/icon-box/style.css rename to full-stack-army/examples/icon-box/style.css diff --git a/live-classes/README.md b/full-stack-army/live-classes/README.md similarity index 100% rename from live-classes/README.md rename to full-stack-army/live-classes/README.md diff --git a/projects/README.md b/full-stack-army/projects/README.md similarity index 100% rename from projects/README.md rename to full-stack-army/projects/README.md diff --git a/projects/attendance-system/server/.gitignore b/full-stack-army/projects/attendance-system/server/.gitignore similarity index 100% rename from projects/attendance-system/server/.gitignore rename to full-stack-army/projects/attendance-system/server/.gitignore diff --git a/projects/attendance-system/server/controller/admin-attendance.js b/full-stack-army/projects/attendance-system/server/controller/admin-attendance.js similarity index 100% rename from projects/attendance-system/server/controller/admin-attendance.js rename to full-stack-army/projects/attendance-system/server/controller/admin-attendance.js diff --git a/projects/attendance-system/server/controller/auth.js b/full-stack-army/projects/attendance-system/server/controller/auth.js similarity index 100% rename from projects/attendance-system/server/controller/auth.js rename to full-stack-army/projects/attendance-system/server/controller/auth.js diff --git a/projects/attendance-system/server/controller/student-attendance.js b/full-stack-army/projects/attendance-system/server/controller/student-attendance.js similarity index 100% rename from projects/attendance-system/server/controller/student-attendance.js rename to full-stack-army/projects/attendance-system/server/controller/student-attendance.js diff --git a/projects/attendance-system/server/controller/users.js b/full-stack-army/projects/attendance-system/server/controller/users.js similarity index 100% rename from projects/attendance-system/server/controller/users.js rename to full-stack-army/projects/attendance-system/server/controller/users.js diff --git a/projects/attendance-system/server/db.js b/full-stack-army/projects/attendance-system/server/db.js similarity index 100% rename from projects/attendance-system/server/db.js rename to full-stack-army/projects/attendance-system/server/db.js diff --git a/projects/attendance-system/server/middleware/authenticate.js b/full-stack-army/projects/attendance-system/server/middleware/authenticate.js similarity index 100% rename from projects/attendance-system/server/middleware/authenticate.js rename to full-stack-army/projects/attendance-system/server/middleware/authenticate.js diff --git a/projects/attendance-system/server/models/AdminAttendance.js b/full-stack-army/projects/attendance-system/server/models/AdminAttendance.js similarity index 100% rename from projects/attendance-system/server/models/AdminAttendance.js rename to full-stack-army/projects/attendance-system/server/models/AdminAttendance.js diff --git a/projects/attendance-system/server/models/Profile.js b/full-stack-army/projects/attendance-system/server/models/Profile.js similarity index 100% rename from projects/attendance-system/server/models/Profile.js rename to full-stack-army/projects/attendance-system/server/models/Profile.js diff --git a/projects/attendance-system/server/models/StudentAttendance.js b/full-stack-army/projects/attendance-system/server/models/StudentAttendance.js similarity index 100% rename from projects/attendance-system/server/models/StudentAttendance.js rename to full-stack-army/projects/attendance-system/server/models/StudentAttendance.js diff --git a/projects/attendance-system/server/models/User.js b/full-stack-army/projects/attendance-system/server/models/User.js similarity index 100% rename from projects/attendance-system/server/models/User.js rename to full-stack-army/projects/attendance-system/server/models/User.js diff --git a/projects/attendance-system/server/package.json b/full-stack-army/projects/attendance-system/server/package.json similarity index 100% rename from projects/attendance-system/server/package.json rename to full-stack-army/projects/attendance-system/server/package.json diff --git a/projects/attendance-system/server/routes/admin-attendance.js b/full-stack-army/projects/attendance-system/server/routes/admin-attendance.js similarity index 100% rename from projects/attendance-system/server/routes/admin-attendance.js rename to full-stack-army/projects/attendance-system/server/routes/admin-attendance.js diff --git a/projects/attendance-system/server/routes/auth.js b/full-stack-army/projects/attendance-system/server/routes/auth.js similarity index 100% rename from projects/attendance-system/server/routes/auth.js rename to full-stack-army/projects/attendance-system/server/routes/auth.js diff --git a/projects/attendance-system/server/routes/index.js b/full-stack-army/projects/attendance-system/server/routes/index.js similarity index 100% rename from projects/attendance-system/server/routes/index.js rename to full-stack-army/projects/attendance-system/server/routes/index.js diff --git a/projects/attendance-system/server/routes/student-attendance.js b/full-stack-army/projects/attendance-system/server/routes/student-attendance.js similarity index 100% rename from projects/attendance-system/server/routes/student-attendance.js rename to full-stack-army/projects/attendance-system/server/routes/student-attendance.js diff --git a/projects/attendance-system/server/routes/users.js b/full-stack-army/projects/attendance-system/server/routes/users.js similarity index 100% rename from projects/attendance-system/server/routes/users.js rename to full-stack-army/projects/attendance-system/server/routes/users.js diff --git a/projects/attendance-system/server/server.js b/full-stack-army/projects/attendance-system/server/server.js similarity index 100% rename from projects/attendance-system/server/server.js rename to full-stack-army/projects/attendance-system/server/server.js diff --git a/projects/attendance-system/server/service/auth.js b/full-stack-army/projects/attendance-system/server/service/auth.js similarity index 100% rename from projects/attendance-system/server/service/auth.js rename to full-stack-army/projects/attendance-system/server/service/auth.js diff --git a/projects/attendance-system/server/service/user.js b/full-stack-army/projects/attendance-system/server/service/user.js similarity index 100% rename from projects/attendance-system/server/service/user.js rename to full-stack-army/projects/attendance-system/server/service/user.js diff --git a/projects/attendance-system/server/utils/error.js b/full-stack-army/projects/attendance-system/server/utils/error.js similarity index 100% rename from projects/attendance-system/server/utils/error.js rename to full-stack-army/projects/attendance-system/server/utils/error.js diff --git a/projects/attendance-system/server/yarn.lock b/full-stack-army/projects/attendance-system/server/yarn.lock similarity index 100% rename from projects/attendance-system/server/yarn.lock rename to full-stack-army/projects/attendance-system/server/yarn.lock diff --git a/projects/raffle-draw/app/app.js b/full-stack-army/projects/raffle-draw/app/app.js similarity index 100% rename from projects/raffle-draw/app/app.js rename to full-stack-army/projects/raffle-draw/app/app.js diff --git a/projects/raffle-draw/app/error.js b/full-stack-army/projects/raffle-draw/app/error.js similarity index 100% rename from projects/raffle-draw/app/error.js rename to full-stack-army/projects/raffle-draw/app/error.js diff --git a/projects/raffle-draw/app/middleware.js b/full-stack-army/projects/raffle-draw/app/middleware.js similarity index 100% rename from projects/raffle-draw/app/middleware.js rename to full-stack-army/projects/raffle-draw/app/middleware.js diff --git a/projects/raffle-draw/app/routes.js b/full-stack-army/projects/raffle-draw/app/routes.js similarity index 100% rename from projects/raffle-draw/app/routes.js rename to full-stack-army/projects/raffle-draw/app/routes.js diff --git a/projects/raffle-draw/db/db.js b/full-stack-army/projects/raffle-draw/db/db.js similarity index 100% rename from projects/raffle-draw/db/db.js rename to full-stack-army/projects/raffle-draw/db/db.js diff --git a/projects/raffle-draw/default.env b/full-stack-army/projects/raffle-draw/default.env similarity index 100% rename from projects/raffle-draw/default.env rename to full-stack-army/projects/raffle-draw/default.env diff --git a/projects/raffle-draw/models/Ticket.js b/full-stack-army/projects/raffle-draw/models/Ticket.js similarity index 100% rename from projects/raffle-draw/models/Ticket.js rename to full-stack-army/projects/raffle-draw/models/Ticket.js diff --git a/projects/raffle-draw/package.json b/full-stack-army/projects/raffle-draw/package.json similarity index 100% rename from projects/raffle-draw/package.json rename to full-stack-army/projects/raffle-draw/package.json diff --git a/projects/raffle-draw/public/index.html b/full-stack-army/projects/raffle-draw/public/index.html similarity index 100% rename from projects/raffle-draw/public/index.html rename to full-stack-army/projects/raffle-draw/public/index.html diff --git a/projects/raffle-draw/requirements.md b/full-stack-army/projects/raffle-draw/requirements.md similarity index 100% rename from projects/raffle-draw/requirements.md rename to full-stack-army/projects/raffle-draw/requirements.md diff --git a/projects/raffle-draw/routes/ticket.js b/full-stack-army/projects/raffle-draw/routes/ticket.js similarity index 100% rename from projects/raffle-draw/routes/ticket.js rename to full-stack-army/projects/raffle-draw/routes/ticket.js diff --git a/projects/raffle-draw/server.js b/full-stack-army/projects/raffle-draw/server.js similarity index 100% rename from projects/raffle-draw/server.js rename to full-stack-army/projects/raffle-draw/server.js diff --git a/projects/raffle-draw/test/test.js b/full-stack-army/projects/raffle-draw/test/test.js similarity index 100% rename from projects/raffle-draw/test/test.js rename to full-stack-army/projects/raffle-draw/test/test.js diff --git a/projects/raffle-draw/yarn.lock b/full-stack-army/projects/raffle-draw/yarn.lock similarity index 100% rename from projects/raffle-draw/yarn.lock rename to full-stack-army/projects/raffle-draw/yarn.lock diff --git a/full-stack-army/references/README.md b/full-stack-army/references/README.md new file mode 100644 index 0000000..9b92e9b --- /dev/null +++ b/full-stack-army/references/README.md @@ -0,0 +1,22 @@ +# Add Necessary References + +Feel free to update this `README.md` file to add additional resources like youtube videos, youtube playlists, articles, books, and other references. + +## Youtube Videos + +- `CLI` [Command Line Interface For Beginners](https://youtu.be/xF6t9h8iD6I) + +**Youtube Playlists:** + +- `Git` [Git Bangla Tutorial](https://www.youtube.com/playlist?list=PL_XxuZqN0xVDDw5eyzuRDXBzgdnW7UpDF) +- `Development` [Development Essentials - Must Learn](https://www.youtube.com/playlist?list=PL_XxuZqN0xVAebtxbmfZUaq69AS3ST4RZ) +- `JavaScript` [JavaScript All You Need to Know](https://www.youtube.com/playlist?list=PL_XxuZqN0xVAu_dWUVFbscqZdTzE8t6Z1) +- `JavaScript` [Make Fun of JavaScript Array](https://www.youtube.com/playlist?list=PL_XxuZqN0xVDr08QgQHljCecWtA4jBLnS) + +**Articles:** + +**Books:** + +- `FullStack` [FullStack Development - Connecting The Dots](https://www.rokomari.com/book/211527/fullstack-development) + +**Others:** diff --git a/resources/README.md b/full-stack-army/resources/README.md similarity index 100% rename from resources/README.md rename to full-stack-army/resources/README.md diff --git a/resources/lecture-0/README.md b/full-stack-army/resources/lecture-0/README.md similarity index 100% rename from resources/lecture-0/README.md rename to full-stack-army/resources/lecture-0/README.md diff --git a/full-stack-army/resources/lecture-0/lecture0-diagram.jpg b/full-stack-army/resources/lecture-0/lecture0-diagram.jpg new file mode 100644 index 0000000..ed25983 Binary files /dev/null and b/full-stack-army/resources/lecture-0/lecture0-diagram.jpg differ diff --git a/resources/lecture-01/README.md b/full-stack-army/resources/lecture-01/README.md similarity index 100% rename from resources/lecture-01/README.md rename to full-stack-army/resources/lecture-01/README.md diff --git a/resources/lecture-01/ps-caffe/README.md b/full-stack-army/resources/lecture-01/ps-caffe/README.md similarity index 100% rename from resources/lecture-01/ps-caffe/README.md rename to full-stack-army/resources/lecture-01/ps-caffe/README.md diff --git a/resources/lecture-01/sdlc/README.md b/full-stack-army/resources/lecture-01/sdlc/README.md similarity index 100% rename from resources/lecture-01/sdlc/README.md rename to full-stack-army/resources/lecture-01/sdlc/README.md diff --git a/resources/lecture-01/technology/README.md b/full-stack-army/resources/lecture-01/technology/README.md similarity index 100% rename from resources/lecture-01/technology/README.md rename to full-stack-army/resources/lecture-01/technology/README.md diff --git a/resources/lecture-02/README.md b/full-stack-army/resources/lecture-02/README.md similarity index 100% rename from resources/lecture-02/README.md rename to full-stack-army/resources/lecture-02/README.md diff --git a/full-stack-army/resources/lecture-03/Programming Language Landscape.drawio b/full-stack-army/resources/lecture-03/Programming Language Landscape.drawio new file mode 100644 index 0000000..9686ba7 --- /dev/null +++ b/full-stack-army/resources/lecture-03/Programming Language Landscape.drawio @@ -0,0 +1 @@ +<mxfile host="Electron" modified="2022-04-03T06:57:33.743Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/17.2.4 Chrome/96.0.4664.174 Electron/16.1.0 Safari/537.36" etag="r9FiRvo4FeD-XUuJaf2W" version="17.2.4" type="device"><diagram id="ZxNBjd9VcVq2T27Gyzy1" name="Page-1">7Vrbbts4EP0aA8mDA90tPyZ22u3WqY010MsjLTEyW0nUUlRj9euXlEjdfUmsRO6iQAKLwyFFnjkzHIoc6bNg956AaPuAXeiPNMXdjfT5SNNU1ZywHy5Jc4lp6bnAI8gVSqVgjX5BIVSENEEujGuKFGOfoqgudHAYQofWZIAQ/FRXe8R+/a0R8GBLsHaA35Z+QS7dynlZ07LiL4i8rXi1rYkJB0Aqi5nEW+Dip4pIvx/pM4IxzZ+C3Qz6HDyJS97u3Z7aYmAEhvSUButPX74vP83+WawX04fZ6pfyEHwc8wa8m5/AT8SMxWhpKiEgOAldyHtRRvrd0xZRuI6Aw2ufmNGZbEsDn5VU9viIQyqsqLIp3onuIaFwt3fgagEH4xHEAaQkZSqygS4oIyhkqFZefioNYtgC5W3FFpophECQwCv6LnFiDwKqbtgM5717hx4xSJ351x/L8d+rCIzVDtQsn4r51+Cz/k2wrBjHGTK3TEE1ol1ZyZ488Zv1spGCGQ4iHDKgYtYGP/K5sP8VwR4BQYBCj5UWIPQSTmPRmM1o0+yQyfKRSXHDxsw6tG7ImBL8A86wjwmThGwQfBLI9xsi4CMvZEWHDRIy+R23NWIOdCsqAuS6/DWdzKlzaw8zOvizlyymZt+YNbqoltaii2qbbbror8WWto99CKOE0+RqTV328+H6gNOpx52uB9wMRauhpqkdqGkdTmb1gFpnaNJbmECXhWZRxIRusYdD4N+X0gadSp0FxpHA6jukNBURCiQU15FkAJL0K2/PWCSK30R3WWG+q5XSGnP5AA8HOTYfnBAHHmCLmDcFxIP0AD577EmgDyj6WR9Hl3GypreEgLSiEGGUxZqi5xUXlDSx1AZN7El90Wno643Y/Vx9XZ00iJSPuKRVMfWXM80YhGk7RCtEY6VvlZqSZryQVjn3e7BT+x3YOZXZabf+2Ww7hGBlNWDLuQPjOF/Nr9bsDVv8NPySYJoDLgmd0Bkt6JYJra2ky+Fhm1zcSqoNs5S+IMA1dw+VgGfodjXkjZUbRdGOxL2stIIEMRx5bnpGMNy/BJ8SDHuJhu3F0mhsjJRJvYt8YKJVg0Q9rJvtXdA7Tho2JU6UPhzxrH2jNqlHd5lLDOeI8qPGMI6oVtywdMojmYZadTrlZmIecbkevUs70bvMM53rPJOqf0zav0mtQU3a/gT2x6Rnm3QyqEkHzYD6MOkFWtQe1KKDbtr/pxad9pGmPnfTrip6I08zDu/aWw0sZdrg0yt8JGp/xF1Tkjg0IReQ6trTS0t1zRZcy+Xq4nAyZPp2DKc+zgb2Zzr1PVQSOheyg2rBZQ4N16QF1xzG/JxJU1aAsk1+GF8eatOhUbMPoEZQ6KDIh7E8KowjENYAlIeUTn7wx08vibcBV2y07I+NSOl8uuaPHFAlO/p8BAHy07x5gEMcZyaoqZSno0r34ajZvGhgMhC4NDtFL0oSFDODhUnm/JkPzOQ4mAzLY7pqoSsN/6JutLKbHPyiRhrArBJX6ubYF7qSwFzAB1YjMRdmNObyjMilWuXtOaFPmV9RVUytpLjJSV5o6kqJhZJW5JOKPCd92XsFP0n+SodlHYhLuVcZQBP5rFjAXxXWSSH0Wuwpzsdz3u85Hx86hpi6IY+1B4si01YUefalB63Lr+drXnf1iVtLcWEEQze77ZCHp66bDvH1qfca3txu8h6KtJvdEftl+lq1mmm9ktXUdib2GRAENjzCDIyWamuN/EIZOG1V2/nYmgIKgyxEKrxzTbnfRYSf13GGNvegvo+iGL4JerrWQM84ET371dBrp2fLCBJAWd7QRCregog/svkC34d+5ucMmKhyXFOrq5zjHL2EhnZQ3iXsCWzDujiqtrO6BfaQ83JOgjjKr1Bm+PV0CNM4DTWtNm5dd/dej6PtZUzuuNrxcIuDTRK/jTcbFwaUJHMFKPFph6f3H2E6/pyntCyBRcy/lfl6r5O72EmyEHoUyU2+BC02PULb9N0uaPsKlCORd1a+PZUZp37/Hw==</diagram></mxfile> \ No newline at end of file diff --git a/full-stack-army/resources/lecture-03/Programming Language Landscape.png b/full-stack-army/resources/lecture-03/Programming Language Landscape.png new file mode 100644 index 0000000..e211fac Binary files /dev/null and b/full-stack-army/resources/lecture-03/Programming Language Landscape.png differ diff --git a/resources/lecture-03/README.md b/full-stack-army/resources/lecture-03/README.md similarity index 100% rename from resources/lecture-03/README.md rename to full-stack-army/resources/lecture-03/README.md diff --git a/full-stack-army/resources/lecture-03/app.js b/full-stack-army/resources/lecture-03/app.js new file mode 100644 index 0000000..645cf1b --- /dev/null +++ b/full-stack-army/resources/lecture-03/app.js @@ -0,0 +1,23 @@ +/* let a = 10 + 20 - 1; +let b = 20 + 30 - 1; +let c = 40 + 50 - 1; +let d = 10 + 20 - 1; +let e = 20 + 30 - 1; +let f = 40 + 50 - 1; */ + +let a = myFunction(10, 20); +let b = myFunction(20, 30); +let c = myFunction(40, 50); +let d = myFunction(10, 20); +let e = myFunction(20, 30); +let f = myFunction(40, 50); + +function myFunction(a, b) { + // return a + b - 1; + // return a + b; + return a ** a + b ** b; +} + +/** + * If we have data, make a variable for it + */ diff --git a/resources/lecture-04/README.md b/full-stack-army/resources/lecture-04/README.md similarity index 100% rename from resources/lecture-04/README.md rename to full-stack-army/resources/lecture-04/README.md diff --git a/resources/lecture-05/README.md b/full-stack-army/resources/lecture-05/README.md similarity index 100% rename from resources/lecture-05/README.md rename to full-stack-army/resources/lecture-05/README.md diff --git a/full-stack-army/resources/lecture-05/img/1.jpeg b/full-stack-army/resources/lecture-05/img/1.jpeg new file mode 100644 index 0000000..4ff37bb Binary files /dev/null and b/full-stack-army/resources/lecture-05/img/1.jpeg differ diff --git a/full-stack-army/resources/lecture-05/img/10.jpeg b/full-stack-army/resources/lecture-05/img/10.jpeg new file mode 100644 index 0000000..a8d33ee Binary files /dev/null and b/full-stack-army/resources/lecture-05/img/10.jpeg differ diff --git a/full-stack-army/resources/lecture-05/img/11.jpeg b/full-stack-army/resources/lecture-05/img/11.jpeg new file mode 100644 index 0000000..1a6a14b Binary files /dev/null and b/full-stack-army/resources/lecture-05/img/11.jpeg differ diff --git a/full-stack-army/resources/lecture-05/img/2.jpeg b/full-stack-army/resources/lecture-05/img/2.jpeg new file mode 100644 index 0000000..a31fdcc Binary files /dev/null and b/full-stack-army/resources/lecture-05/img/2.jpeg differ diff --git a/full-stack-army/resources/lecture-05/img/3.jpeg b/full-stack-army/resources/lecture-05/img/3.jpeg new file mode 100644 index 0000000..745a60b Binary files /dev/null and b/full-stack-army/resources/lecture-05/img/3.jpeg differ diff --git a/full-stack-army/resources/lecture-05/img/4.jpeg b/full-stack-army/resources/lecture-05/img/4.jpeg new file mode 100644 index 0000000..9f19a44 Binary files /dev/null and b/full-stack-army/resources/lecture-05/img/4.jpeg differ diff --git a/full-stack-army/resources/lecture-05/img/5.jpeg b/full-stack-army/resources/lecture-05/img/5.jpeg new file mode 100644 index 0000000..26ac6b6 Binary files /dev/null and b/full-stack-army/resources/lecture-05/img/5.jpeg differ diff --git a/full-stack-army/resources/lecture-05/img/6.jpeg b/full-stack-army/resources/lecture-05/img/6.jpeg new file mode 100644 index 0000000..a29d8d5 Binary files /dev/null and b/full-stack-army/resources/lecture-05/img/6.jpeg differ diff --git a/full-stack-army/resources/lecture-05/img/7.jpeg b/full-stack-army/resources/lecture-05/img/7.jpeg new file mode 100644 index 0000000..f7b0ea5 Binary files /dev/null and b/full-stack-army/resources/lecture-05/img/7.jpeg differ diff --git a/full-stack-army/resources/lecture-05/img/8.jpeg b/full-stack-army/resources/lecture-05/img/8.jpeg new file mode 100644 index 0000000..76ac655 Binary files /dev/null and b/full-stack-army/resources/lecture-05/img/8.jpeg differ diff --git a/full-stack-army/resources/lecture-05/img/9.jpeg b/full-stack-army/resources/lecture-05/img/9.jpeg new file mode 100644 index 0000000..27f6df0 Binary files /dev/null and b/full-stack-army/resources/lecture-05/img/9.jpeg differ diff --git a/resources/lecture-06/README.md b/full-stack-army/resources/lecture-06/README.md similarity index 100% rename from resources/lecture-06/README.md rename to full-stack-army/resources/lecture-06/README.md diff --git a/full-stack-army/resources/lecture-07/README.md b/full-stack-army/resources/lecture-07/README.md new file mode 100644 index 0000000..c098255 --- /dev/null +++ b/full-stack-army/resources/lecture-07/README.md @@ -0,0 +1,26 @@ +# Lecture 7 - QNA 1 - Don't Miss The Last Part + +## Important Links + +- **For Development Essentials** + - [Development Essentials - Must Learn](https://youtube.com/playlist?list=PL_XxuZqN0xVAebtxbmfZUaq69AS3ST4RZ) +- **For Problem Solving** + - [Leetcode](https://leetcode.com/) + - [GeeksForGeeks](https://www.geeksforgeeks.org/) + - [San Foundry](https://www.sanfoundry.com/) + - [Hackerrank](https://www.hackerrank.com/) +- **For Digital Electronics and Discrete Mathematics** + - [Neso Academy](https://www.youtube.com/c/nesoacademy) + +### Book list + +- **Must Read** + - [Computer Science Distilled (For non CSE person)](./images/computer-science-distilled.jpg) + - [Elements of Programming Interviews](./images/elements-of-programming-interviews.jpg) + - [Introduction to Algorithms](./images/introduction-to-algorithms.jpg) +- **For JavaScript** + - [You Don't Know JS Series](./images/you-dont-know-js.jpg) + - [Javascript: The Good Parts](./images/js-the-good-parts.jpg) + - [Node.js 8 The Right Way](./images/nodejs-8-the-right-way.jpg) + - [JavaScript: The Definitive Guide](./images/js-the-definitive-guide.jpg) + - [JavaScript Cookbook](./images/js-cookbook.jpg) diff --git a/full-stack-army/resources/lecture-07/images/computer-science-distilled.jpg b/full-stack-army/resources/lecture-07/images/computer-science-distilled.jpg new file mode 100644 index 0000000..ab65778 Binary files /dev/null and b/full-stack-army/resources/lecture-07/images/computer-science-distilled.jpg differ diff --git a/full-stack-army/resources/lecture-07/images/elements-of-programming-interviews.jpg b/full-stack-army/resources/lecture-07/images/elements-of-programming-interviews.jpg new file mode 100644 index 0000000..63b20d1 Binary files /dev/null and b/full-stack-army/resources/lecture-07/images/elements-of-programming-interviews.jpg differ diff --git a/full-stack-army/resources/lecture-07/images/head-first-js-programming.jpg b/full-stack-army/resources/lecture-07/images/head-first-js-programming.jpg new file mode 100644 index 0000000..a240d4c Binary files /dev/null and b/full-stack-army/resources/lecture-07/images/head-first-js-programming.jpg differ diff --git a/full-stack-army/resources/lecture-07/images/introduction-to-algorithms.jpg b/full-stack-army/resources/lecture-07/images/introduction-to-algorithms.jpg new file mode 100644 index 0000000..f331a69 Binary files /dev/null and b/full-stack-army/resources/lecture-07/images/introduction-to-algorithms.jpg differ diff --git a/full-stack-army/resources/lecture-07/images/js-cookbook.jpg b/full-stack-army/resources/lecture-07/images/js-cookbook.jpg new file mode 100644 index 0000000..fff02a0 Binary files /dev/null and b/full-stack-army/resources/lecture-07/images/js-cookbook.jpg differ diff --git a/full-stack-army/resources/lecture-07/images/js-the-definitive-guide.jpg b/full-stack-army/resources/lecture-07/images/js-the-definitive-guide.jpg new file mode 100644 index 0000000..8426e75 Binary files /dev/null and b/full-stack-army/resources/lecture-07/images/js-the-definitive-guide.jpg differ diff --git a/full-stack-army/resources/lecture-07/images/js-the-good-parts.jpg b/full-stack-army/resources/lecture-07/images/js-the-good-parts.jpg new file mode 100644 index 0000000..6951868 Binary files /dev/null and b/full-stack-army/resources/lecture-07/images/js-the-good-parts.jpg differ diff --git a/full-stack-army/resources/lecture-07/images/nodejs-8-the-right-way.jpg b/full-stack-army/resources/lecture-07/images/nodejs-8-the-right-way.jpg new file mode 100644 index 0000000..bfcb93b Binary files /dev/null and b/full-stack-army/resources/lecture-07/images/nodejs-8-the-right-way.jpg differ diff --git a/full-stack-army/resources/lecture-07/images/you-dont-know-js.jpg b/full-stack-army/resources/lecture-07/images/you-dont-know-js.jpg new file mode 100644 index 0000000..4b8f165 Binary files /dev/null and b/full-stack-army/resources/lecture-07/images/you-dont-know-js.jpg differ diff --git a/resources/lecture-08/README.md b/full-stack-army/resources/lecture-08/README.md similarity index 100% rename from resources/lecture-08/README.md rename to full-stack-army/resources/lecture-08/README.md diff --git a/resources/lecture-09/README.md b/full-stack-army/resources/lecture-09/README.md similarity index 100% rename from resources/lecture-09/README.md rename to full-stack-army/resources/lecture-09/README.md diff --git a/resources/lecture-10/README.md b/full-stack-army/resources/lecture-10/README.md similarity index 100% rename from resources/lecture-10/README.md rename to full-stack-army/resources/lecture-10/README.md diff --git a/full-stack-army/resources/lecture-10/event-loop.gif b/full-stack-army/resources/lecture-10/event-loop.gif new file mode 100644 index 0000000..5cae5b0 Binary files /dev/null and b/full-stack-army/resources/lecture-10/event-loop.gif differ diff --git a/resources/lecture-11/README.md b/full-stack-army/resources/lecture-11/README.md similarity index 100% rename from resources/lecture-11/README.md rename to full-stack-army/resources/lecture-11/README.md diff --git a/resources/lecture-12/README.md b/full-stack-army/resources/lecture-12/README.md similarity index 100% rename from resources/lecture-12/README.md rename to full-stack-army/resources/lecture-12/README.md diff --git a/resources/lecture-12/lec-12.drawio b/full-stack-army/resources/lecture-12/lec-12.drawio similarity index 100% rename from resources/lecture-12/lec-12.drawio rename to full-stack-army/resources/lecture-12/lec-12.drawio diff --git a/full-stack-army/resources/lecture-12/visualize-our-model.jpg b/full-stack-army/resources/lecture-12/visualize-our-model.jpg new file mode 100644 index 0000000..2736b19 Binary files /dev/null and b/full-stack-army/resources/lecture-12/visualize-our-model.jpg differ diff --git a/resources/lecture-13/README.md b/full-stack-army/resources/lecture-13/README.md similarity index 100% rename from resources/lecture-13/README.md rename to full-stack-army/resources/lecture-13/README.md diff --git a/full-stack-army/resources/lecture-14/README.md b/full-stack-army/resources/lecture-14/README.md new file mode 100644 index 0000000..9157d06 --- /dev/null +++ b/full-stack-army/resources/lecture-14/README.md @@ -0,0 +1,26 @@ +# Lecture 14 - Backend 1 | Course planning and discussion + +## Curriculum: + +- ExpressJS +- MongoDB +- PostgreSQL +- REST API +- GraphQL +- Application Building Process + - Architecture + - Cloud + - Documentation + - Testing + - Unit Testing + - Acceptance Testing + - Caching + - Email and SMS + - Event Driven Development + - Distributed Login +- Serverless + - No code low code + +### Task + +- What is Backend Development? What is the responsibilities of Backend developer? Make a research paper about backend journey. diff --git a/full-stack-army/resources/lecture-15/README.md b/full-stack-army/resources/lecture-15/README.md new file mode 100644 index 0000000..debdefb --- /dev/null +++ b/full-stack-army/resources/lecture-15/README.md @@ -0,0 +1,91 @@ +# Lecture 15 - [Backend 2] Introduction to Backend Development + +## Topics to learn + +**Must Needed** + +- API Design + - REST API + - GraphQL + - gRPC + - <p style='color: gray;'>SOAP (less than 1%)</p> + - <p style='color: gray;'>Web Socket (really tough to scale)</p> + - <p style='color: gray;'> Message Broker (Not beginner friendly concept)</p> +- API Security + - JWT Token + - Refresh Token + - <p style='color: gray;'>OAuth2</p> + - <p style='color: gray;'>SAML</p> + - <p style='color: gray;'>Identity Providers (Cognito, Auth0, Firebase, Okta)</p> + - Role Based Authorization +- API Testing + - Unit Testing + - Acceptance Testing + - Load Testing +- API Documentation + - Swagger + - Postman + +To learn above 4 topics we need to learn some another topics: + +- Database + - NoSQL + - MongoDB + - AWS DynamoDB + - SQL + - PostgreSQL + - MySQL + - MSSQL / Oracle + - In Memory\* + - Redis\* + - Mem Cached + - Graph Database + - Neo4j +- Linux Server +- Cloud Computing +- DevOps + +### Server Application Responsibilities + +- Listen Request + - Always Same +- Process + - Algorithm + - Data Structure + - Database + - Problem Solving + - CRUD +- Response + - Always Same + +#### HTTP (Hypertext Transfer Protocol) + +- GET - want to read data from server +- POST - create new data +- PUT/PATCH - update existing content +- DELETE - delete data from database + +#### Routing Pattern + +- GET Everything - /books +- Get one book - /books/bookId +- POST new book - /books +- Update Book - /books/bookId +- Delete Book - /book/booksId + +#### Pipeline + +REQUEST -> MIDDLEWARE[logger, body parser, file parser, user ip, block ip, authentication, authorization, validation] -> CONTROLLER (Business Logic) -> MIDDLEWARE[error handler] -> RESPONSE + +#### References + +- [ExpressJS API References](https://expressjs.com/en/4x/api.html) +- [Source Code](../../src/lecture-15/) + +#### Tasks + +Complete the playlist [Express Js Crash Course In Bangla](https://youtube.com/playlist?list=PL_XxuZqN0xVDm9HkiP4h_76qNBZix6XME) + +#### Next week planning + +- We will create our first working REST API. diff --git a/resources/lecture-16/README.md b/full-stack-army/resources/lecture-16/README.md similarity index 100% rename from resources/lecture-16/README.md rename to full-stack-army/resources/lecture-16/README.md diff --git a/resources/lecture-17/README.md b/full-stack-army/resources/lecture-17/README.md similarity index 100% rename from resources/lecture-17/README.md rename to full-stack-army/resources/lecture-17/README.md diff --git a/resources/lecture-18/README.md b/full-stack-army/resources/lecture-18/README.md similarity index 100% rename from resources/lecture-18/README.md rename to full-stack-army/resources/lecture-18/README.md diff --git a/full-stack-army/resources/lecture-19/README.md b/full-stack-army/resources/lecture-19/README.md new file mode 100644 index 0000000..a11033c --- /dev/null +++ b/full-stack-army/resources/lecture-19/README.md @@ -0,0 +1,3 @@ +# Lecture 19 - [Backend 6] Adda with Random Topics | You can Skip + +This video was part of our backend BootCamp. This lecture was given after the Stack Learner's youtube channel got hacked. This is just an adda that may motivate you but you can skip it. diff --git a/resources/lecture-20/README.md b/full-stack-army/resources/lecture-20/README.md similarity index 100% rename from resources/lecture-20/README.md rename to full-stack-army/resources/lecture-20/README.md diff --git a/full-stack-army/resources/lecture-22/README.md b/full-stack-army/resources/lecture-22/README.md new file mode 100644 index 0000000..fced5b2 --- /dev/null +++ b/full-stack-army/resources/lecture-22/README.md @@ -0,0 +1,68 @@ +# Lecture 22 - Authentication System from Pseudo Code to Real Code + +## Pseudo Code + +### Authentication + +**Registration Process:** + +```txt +Start +name = input() +email = input() +password = input() +if name && email && password is invalid: + return 400 error + +user = find user with email +if user found: + return 400 error + +hash = hash password +user = save name, email, hash to user model +return 201 +End +``` + +**Login Process:** + +```txt +Start +email = input() +password = input() + +user = find user with email +if user not found: + return 400 error + +if password not equal to user hash: + return 400 error + +token = generate token using user +return token +End +``` + +**Reset Password:** + +```txt +Start +new-password = Input() +old-password = Input() +TODO + I +if old-password not equal to user.hash password: + return 400 error +else hashNewPassword = hash new-password + save hashNewPassword + return 201 +End +``` + +--- + +**Progress:** +Follow [this link](https://thirsty-camelotia-a8e.notion.site/Attendance-System-8b5ccfe9b2384e84b904d6a85013170b) to check the progress + +**Source Code** +[Click Here](../../src/attendance-system/) diff --git a/src/README.md b/full-stack-army/src/README.md similarity index 100% rename from src/README.md rename to full-stack-army/src/README.md diff --git a/src/attendance-system/server/db.js b/full-stack-army/src/attendance-system/server/db.js similarity index 100% rename from src/attendance-system/server/db.js rename to full-stack-army/src/attendance-system/server/db.js diff --git a/src/lecture-13/attendance-system/server/models/AdminAttendance.js b/full-stack-army/src/attendance-system/server/models/AdminAttendance.js similarity index 100% rename from src/lecture-13/attendance-system/server/models/AdminAttendance.js rename to full-stack-army/src/attendance-system/server/models/AdminAttendance.js diff --git a/src/lecture-13/attendance-system/server/models/Profile.js b/full-stack-army/src/attendance-system/server/models/Profile.js similarity index 100% rename from src/lecture-13/attendance-system/server/models/Profile.js rename to full-stack-army/src/attendance-system/server/models/Profile.js diff --git a/src/lecture-13/attendance-system/server/models/StudentAttendance.js b/full-stack-army/src/attendance-system/server/models/StudentAttendance.js similarity index 100% rename from src/lecture-13/attendance-system/server/models/StudentAttendance.js rename to full-stack-army/src/attendance-system/server/models/StudentAttendance.js diff --git a/src/attendance-system/server/models/User.js b/full-stack-army/src/attendance-system/server/models/User.js similarity index 100% rename from src/attendance-system/server/models/User.js rename to full-stack-army/src/attendance-system/server/models/User.js diff --git a/src/attendance-system/server/package.json b/full-stack-army/src/attendance-system/server/package.json similarity index 100% rename from src/attendance-system/server/package.json rename to full-stack-army/src/attendance-system/server/package.json diff --git a/src/attendance-system/server/server.js b/full-stack-army/src/attendance-system/server/server.js similarity index 100% rename from src/attendance-system/server/server.js rename to full-stack-army/src/attendance-system/server/server.js diff --git a/src/attendance-system/server/yarn.lock b/full-stack-army/src/attendance-system/server/yarn.lock similarity index 100% rename from src/attendance-system/server/yarn.lock rename to full-stack-army/src/attendance-system/server/yarn.lock diff --git a/src/lecture-04/array.js b/full-stack-army/src/lecture-04/array.js similarity index 100% rename from src/lecture-04/array.js rename to full-stack-army/src/lecture-04/array.js diff --git a/src/lecture-04/condition.js b/full-stack-army/src/lecture-04/condition.js similarity index 100% rename from src/lecture-04/condition.js rename to full-stack-army/src/lecture-04/condition.js diff --git a/src/lecture-04/function.js b/full-stack-army/src/lecture-04/function.js similarity index 100% rename from src/lecture-04/function.js rename to full-stack-army/src/lecture-04/function.js diff --git a/src/lecture-04/loop.js b/full-stack-army/src/lecture-04/loop.js similarity index 100% rename from src/lecture-04/loop.js rename to full-stack-army/src/lecture-04/loop.js diff --git a/src/lecture-04/operatorNotes.txt b/full-stack-army/src/lecture-04/operatorNotes.txt similarity index 100% rename from src/lecture-04/operatorNotes.txt rename to full-stack-army/src/lecture-04/operatorNotes.txt diff --git a/src/lecture-04/variables/dynamicVariable.js b/full-stack-army/src/lecture-04/variables/dynamicVariable.js similarity index 100% rename from src/lecture-04/variables/dynamicVariable.js rename to full-stack-army/src/lecture-04/variables/dynamicVariable.js diff --git a/src/lecture-04/variables/staticVariable.js b/full-stack-army/src/lecture-04/variables/staticVariable.js similarity index 100% rename from src/lecture-04/variables/staticVariable.js rename to full-stack-army/src/lecture-04/variables/staticVariable.js diff --git a/full-stack-army/src/lecture-05/Array Traverse/declarativeWay.js b/full-stack-army/src/lecture-05/Array Traverse/declarativeWay.js new file mode 100644 index 0000000..fdeaad8 --- /dev/null +++ b/full-stack-army/src/lecture-05/Array Traverse/declarativeWay.js @@ -0,0 +1,44 @@ +const numbers = [1, 2, 3, 4, 5]; + +// sum of array elements +sum = 0; +numbers.forEach((element) => { + sum += element; +}); +console.log(sum); + + +const cb = () => { + console.log('Hello') +} +numbers.forEach(cb) +// Hello +// Hello +// Hello +// Hello +// Hello + + +numbers.forEach(function (value, index, arr) { + // console.log(arguments) + console.log(value, index, arr) +}) +// [Arguments] { '0': 1, '1': 0, '2': [ 1, 2, 3, 4, 5 ] } +// [Arguments] { '0': 2, '1': 1, '2': [ 1, 2, 3, 4, 5 ] } +// [Arguments] { '0': 3, '1': 2, '2': [ 1, 2, 3, 4, 5 ] } +// [Arguments] { '0': 4, '1': 3, '2': [ 1, 2, 3, 4, 5 ] } +// [Arguments] { '0': 5, '1': 4, '2': [ 1, 2, 3, 4, 5 ] } + + +numbers.forEach(function (value, _, __) { + if(value % 2 === 0){ + console.log(value) + } +}) + +sum = 0; +numbers.forEach(function (v) { + sum += v +}) + +console.log(sum) diff --git a/full-stack-army/src/lecture-05/Array Traverse/imperativeWay.js b/full-stack-army/src/lecture-05/Array Traverse/imperativeWay.js new file mode 100644 index 0000000..7af39d5 --- /dev/null +++ b/full-stack-army/src/lecture-05/Array Traverse/imperativeWay.js @@ -0,0 +1,6 @@ +const numbers = [1, 2, 3, 4, 5]; + +let sum = 0; +for (let i = 0; i < numbers.length; i++) { + sum += numbers[i]; +} diff --git a/full-stack-army/src/lecture-05/arr.js b/full-stack-army/src/lecture-05/arr.js new file mode 100644 index 0000000..9292659 --- /dev/null +++ b/full-stack-army/src/lecture-05/arr.js @@ -0,0 +1,52 @@ +const arr = [1, 2, 3, null, false, 4, 5, "", "test", 6, 7]; + +let count = 0; +for (let i = 0; i < arr.length; i++) { + for (let j = i; j < arr.length - 1; j++) { + if (!arr[j] || typeof arr[j] !== "number") { + arr[j] = arr[j + 1]; + arr[j + 1] = undefined; + } + } + if (arr[i] === undefined) { + count++; + } +} +arr.length -= count; +console.log(arr); + +// explanation +// arr = [1, false, true, '', 2, 3] +// i = 1, j = 3 +// 1, true, '', 2, 3, undefined +// i = true, j = 3 +// 1, '', 2, 3, undefined, undefined +// i = '', j = 3 +// 1, 2, 3, undefined, undefined, undefined + +// [1, 2, 3] + + +// shortcut +const filteredArray = arr.filter((v) => typeof v === 'number') + +console.log(filteredArray); + + +// using new array +const newArr = [] +for(let i = 0; i < arr.length; i++){ + if(typeof arr[i] === 'number'){ + newArr.push(arr[i]) + } +} +console.log(newArr); + + +// calculate fibonnacci number +function fib(n){ + if(n == 0 || n == 1) return n; + return fib(n - 1) + fib(n - 2) +} + +console.log(fib(10)); \ No newline at end of file diff --git a/full-stack-army/src/lecture-05/delete.js b/full-stack-army/src/lecture-05/delete.js new file mode 100644 index 0000000..cddd561 --- /dev/null +++ b/full-stack-army/src/lecture-05/delete.js @@ -0,0 +1,38 @@ +const arr = [ + { + id: 1, + value: 10, + }, + { + id: 2, + value: 20, + }, + { + id: 3, + value: 30, + }, + { + id: 4, + value: 40, + }, + { + id: 5, + value: 50, + }, +]; + + +// splice ==> mutable +// const index = arr.findIndex(item => { +// item.id === 4 +// }) +// const arr1 = arr.splice(index, 1) +// console.log(arr); + + +// filter ==> immutable +const arr2 = arr.filter(item => { + return item.id !== 4 +}) +console.log(arr); +console.log(arr2); \ No newline at end of file diff --git a/full-stack-army/src/lecture-05/object.js b/full-stack-army/src/lecture-05/object.js new file mode 100644 index 0000000..54af386 --- /dev/null +++ b/full-stack-army/src/lecture-05/object.js @@ -0,0 +1,102 @@ +// object literal +const microphone = { + brand: 'Fantech', + indicator: true, + price: 3400, + color: 'white', + + // methods + startRecording() { + console.log('Recording started'); + }, + stopRecording() { + console.log('Recording stopped'); + } +} + +/** + * There are two different parts in object + * 1. Noun / Adjective (State/data/property/field) + * 2. Verb / (functionalities -> start, stop) + */ + +microphone.startRecording() +microphone.stopRecording() +console.log(microphone); +console.log(Object); + + +// constructor function +const testObj = new Object() +testObj.name = 'Test Object' +testObj.time = new Date() +console.log(testObj); +console.log(testObj.time.getDate()); + +// object k freeze kore dey +// new property add korte dey na +Object.freeze(microphone) +microphone.newProperty = 'hi' +console.log(microphone); + + + +// get key and value +console.log(Object.keys(microphone)); +console.log(Object.values(microphone)); +// [ +// 'brand', +// 'indicator', +// 'price', +// 'color', +// 'startRecording', +// 'stopRecording' +// ] +// [ +// 'Fantech', +// true, +// 3400, +// 'white', +// [Function: startRecording], +// [Function: stopRecording] +// ] + + +// concat function +console.log('micro'.concat('phone')); +console.log('micro' + 'phone'); + + + +// notation +// dot notation -> microphone.brand +// array notation -> microphone[k] +for(let k in microphone){ + console.log(k, microphone[k]); +} +// brand Fantech +// indicator true +// price 3400 +// color white +// startRecording [Function: startRecording] +// stopRecording [Function: stopRecording] + + + +// check is a object is empty or not +const empty ={} +if(Object.keys(empty).length === 0){ + console.log('This object is empty'); +} + + +// object to key value pair +console.log(Object.entries(microphone)); +const array = [ + [ 'brand', 'Fantech' ], + [ 'indicator', true ], + [ 'price', 3400 ], + [ 'color', 'white' ] +] +console.log(Object.fromEntries(array)); +// { brand: 'Fantech', indicator: true, price: 3400, color: 'white' } diff --git a/full-stack-army/src/lecture-05/task.md b/full-stack-army/src/lecture-05/task.md new file mode 100644 index 0000000..4e1ddca --- /dev/null +++ b/full-stack-army/src/lecture-05/task.md @@ -0,0 +1 @@ +- [Make Fun Of Javascript Array](https://youtube.com/playlist?list=PL_XxuZqN0xVDr08QgQHljCecWtA4jBLnS) \ No newline at end of file diff --git a/full-stack-army/src/lecture-05/update.js b/full-stack-army/src/lecture-05/update.js new file mode 100644 index 0000000..1fa577d --- /dev/null +++ b/full-stack-army/src/lecture-05/update.js @@ -0,0 +1,49 @@ +const arr = [ + { + id: 1, + value: 10, + }, + { + id: 2, + value: 20, + }, + { + id: 3, + value: 30, + }, + { + id: 4, + value: 40, + }, + { + id: 5, + value: 50, + }, +]; + +// arr.findIndex ==> not mutable +// const index = arr.findIndex(v => { +// return v.id === 4; +// }) + +// console.log(index); +// console.log(arr); +// arr[index].value = 100 +// console.log(arr); + + + +// arr.find ==> mutable +const obj = arr.find((v) => { + return v.id === 4; +}); +obj.value = 100; +console.log(obj); +console.log(arr); + + + +const obj2 = arr[2] +obj2.value = 300 +console.log(obj); +console.log(arr); \ No newline at end of file diff --git a/full-stack-army/src/lecture-08/app.js b/full-stack-army/src/lecture-08/app.js new file mode 100644 index 0000000..2f3016f --- /dev/null +++ b/full-stack-army/src/lecture-08/app.js @@ -0,0 +1,337 @@ +/** + * * Name: Human_Lifecycle + * * Param: human_name + * * :human_name, awake from sleep + * * :human_name, go to washroom + * * :human_name, take breakfast + * * :human_name, go to school/college/office + * * :human_name, Return from office + * * :human_name, Take dinner + * * :human_name", Go to sleep + */ + +// Call Human_Lifecycle for 'Abu Musa' +// Call Human_Lifecycle for 'Easin Islam' +// Call Human_Lifecycle for 'Saiful Islam' +// Call Human_Lifecycle for 'Akib Ahmed' +// Call Human_Lifecycle for 'Alamin Mir' + +/** + * Function: Sleep + * Param: name + * Definition: How to sleep + */ + +function sleep(name) { + console.log(`${name} is sleeping`); +} + +/** + * Function: Awake + * Param: name + * Definition: How to awake + */ + +function awake(name) { + console.log(`${name} is awake`); +} + +/** + * Function: Eat + * Param: name + * Param: Time + * Definition: How to eat + */ + +function eat(name, time) { + console.log(`${name} is taking ${time}`); +} + +/** + * Function: Walk + * Param: name + * Param: Destination + * Definition: How to walk + */ + +function walk(name, destination) { + console.log(`${name} is walking to ${destination}`); +} + +/** + * Function: Study + * Param: name + * Definition: How to study + */ + +function study(name) { + console.log(`${name} is studying`); +} + +/** + * Function: Work + * Param: name + * Definition: How to work + */ + +function work(name) { + console.log(`${name} is working`); +} + +/** + * Function: Job_Holder_Lifecycle + * Param: name + * Definition: + * - Awake -> name + * - Eat -> name, 'breakfast' + * - Walk -> name, 'office' + * - Work -> name + * - Eat -> name, 'lunch' + * - Walk -> name, 'home' + * - Eat -> name, 'dinner' + * - Sleep -> name + */ + +function jobHolderLifecycle(name) { + awake(name); + eat(name, 'breakfast'); + walk(name, 'office'); + work(name); + eat(name, 'lunch'); + walk(name, 'home'); + eat(name, 'dinner'); + sleep(name); +} + +console.log('Jobholders Lifecycle'); +console.log('**********************'); +jobHolderLifecycle('Shayed Hasan'); +console.log('-----------------------'); +jobHolderLifecycle('Sh Pabel'); +console.log('-----------------------'); +jobHolderLifecycle('Tarikul Islam'); +console.log('-----------------------'); +jobHolderLifecycle('Nahian Sikder'); +console.log('-----------------------'); +jobHolderLifecycle('Mizan Rahman'); +console.log('-----------------------'); + +/** + * Function: Student Lifecycle + * Param: name + * Definition: + * - Awake -> name + * - Eat -> name, 'breakfast' + * - Study -> name + * - Eat -> name, 'lunch' + * - Study -> name + * - Eat -> name, 'dinner' + * - Sleep -> name + */ + +function studentsLifecycle(name) { + awake(name); + eat(name, 'breakfast'); + study(name); + eat(name, 'lunch'); + study(name); + eat(name, 'dinner'); + sleep(name); +} + +console.log('Students Lifecycle'); +console.log('**********************'); +studentsLifecycle('Faruk'); +console.log('--------------------'); +studentsLifecycle('Elias'); +console.log('--------------------'); +studentsLifecycle('Faisal'); +console.log('--------------------'); + +// Students_Lifecycle -> 'Faruk' +// Students_Lifecycle -> 'Elias' +// Students_Lifecycle -> 'Faisal' + +// Job_Holder_Lifecycle -> 'Musa' +// Job_Holder_Lifecycle -> 'Akib' +// Job_Holder_Lifecycle -> 'Bipon' + +// * Function Template +function name_of_the_function(/** Input something */) { + // Function body + // any valid js code + // return a result +} + +// There are two steps +// - Define a function +// - Invoke a function + +/* function testFunction() { + const a = 10; + const b = 20; + const result = a + b + Math.max(a, b); + console.log(result); +} */ + +function testFunction(a = 10, b = 20) { + const result = a + b + Math.max(a, b); + console.log(result); +} + +// testFunction(100, 200); +// testFunction(50, 30); +// testFunction(5); +// testFunction(); + +// function composition +function sum(a, b) { + return a + b; +} + +function subtract(a, b) { + return a - b; +} + +function times(a, b) { + return a * b; +} + +const a = 10; +const b = 20; + +// const r1 = sum(a, b); +// console.log('R1', r1); +// const r2 = subtract(a, b); +// console.log('R2', r2); +const r = Math.abs(times(sum(a, b), subtract(a, b))); +console.log(r); + +// Function definition +// Function Invoking +// Function Parameters/Arguments +// Return result from function + +// Function is a first class citizen +// We can treat function as value +// 10, 'test', true, false -> function + +/** + * * Benefits: + * * - we can store functions in a variable + * * - we can store function inside an object / array + * * - we can pass function as an argument + * * - we can also return a function from another function + */ + +// * Proof -> Function is a value +function testFunction() { + console.log('I am a test function'); +} + +// * store function in a variable +const fn = testFunction; +console.log(fn.toString()); +fn(); + +// * store function inside an object / array +const arr = [fn, testFunction]; +const obj = { + fn: testFunction, +}; + +// * pass function as an argument +function fnArgument(fn) { + return fn(); +} +fnArgument(testFunction); + +// * return a function from another function +function returnFn() { + return testFunction; +} + +// * Let's construct a function +const newFn = new Function( + 'str', + `let obj = {}; + for (let s of str) { + if (s !== ' ') { + obj[s] = s; + } + } + return obj;` +); + +console.log(newFn('HM Nayem')); + +const fData = { + params: ['a', 'b'], + body: ['const r = a + b', 'return r'], +}; + +const fBody = fData.body.reduce((acc, cur) => { + acc += cur + ';'; + return acc; +}, ''); + +const tf = new Function(...fData.params, fBody); +console.log(tf(100, 200)); + +const greetFn = new Function( + 'name', + 'email', + ` + const fName = name.split(' ')[0]; + console.log('Hello,', fName, 'Is that your email?', email); + console.log('Yeah, this is mine.'); + return fName; + ` +); + +console.log(typeof greetFn); +console.log(greetFn.__proto__); +// console.log(greetFn.toString()); +const fName = greetFn('HM Nayem', 'nayem@gmail.com'); +console.log('First Name:', fName); + +const operations = [ + { + args: [10, 20], + params: ['a', 'b'], + body: 'console.log("a + b",a + b)', + }, + { + args: [10, 20], + params: ['a', 'b'], + body: 'console.log("a - b",a - b)', + }, + { + args: [10, 20], + params: ['a', 'b'], + body: 'console.log("a * b",a * b)', + }, + { + args: [], + params: [], + body: 'console.log("Hello World! No params, no args")', + }, + { + args: [5], + params: ['n'], + body: ` + for (let i = 0; i < n; i++) { + let line = ''; + for (let j = 0; j < n; j++) { + line += '* '; + } + console.log(line); + } + `, + }, +]; + +operations.forEach((operation) => { + const fn = new Function(...operation.params, operation.body); + fn(...operation.args); +}); diff --git a/full-stack-army/src/lecture-09/app.js b/full-stack-army/src/lecture-09/app.js new file mode 100644 index 0000000..5b95e39 --- /dev/null +++ b/full-stack-army/src/lecture-09/app.js @@ -0,0 +1,172 @@ +// function statement +function func() {} + +// Function expression +const myFn = function () {}; + +// Fat Arrow function +const myFatArrowFn = () => {}; + +// Pure Function +function sum(a, b) { + return a + b; +} + +sum(10, 20); // 30 + +// // Side effect +// let limit = 100; +// function changeLimit() { +// limit = 500; +// } + +// console.log(changeLimit(limit)); // undefined +// console.log(limit); // 500 + +// Pure +// const arr = [1, 2, 3]; +// function add(arr, data) { +// arr = [...arr, data] +// return arr; +// } + +// Side Effect +const arr = [1, 2, 3]; +function add(data) { + arr.push(data); +} + +// Impure Function +function log(msg) { + console.log(msg); +} + +// Higher order function +function generateTwoRandNumber(max, cb) { + const random1 = Math.floor(Math.random() * max); + const random2 = Math.floor(Math.random() * max); + const result = cb(random1, random2); + return result; +} +// const cb = function (rand1, rand2) { +// console.log(rand1, rand2); +// }; +// generateTwoRandNumber(100, cb); + +// console.log(generateTwoRandNumber(1000, (rand1, rand2) => rand1 + rand2)); +// console.log(generateTwoRandNumber(10, (rand1, rand2) => rand1 * rand2)); +// console.log( +// generateTwoRandNumber(10, (rand1, rand2) => rand1 * rand1 + rand2 * rand2) +// ); + +// function randomSum(max) { +// const random1 = Math.floor(Math.random() * max); +// const random2 = Math.floor(Math.random() * max); +// return random1 + random2; // placeholder +// } + +// function randomSub(max) { +// const random1 = Math.floor(Math.random() * max); +// const random2 = Math.floor(Math.random() * max); +// return random1 - random2; // placeholder +// } + +// function randomSqrSum(max) { +// const random1 = Math.floor(Math.random() * max); +// const random2 = Math.floor(Math.random() * max); +// return random1 * random1 + random2 * random2; // placeholder +// } + +// function sqr(n) { +// return n * n; +// } + +// function cube(n) { +// return n * n * n; +// } + +function power(p) { + return function (n) { + let result = 1; + for (let i = 1; i <= p; i++) { + result *= n; + } + return result; + }; +} + +const sqr = power(2); +const cube = power(3); +const power8 = power(8); +console.log('SQR', sqr); +console.log('CUBE', cube); +console.log('Power8', power8); + +console.log(power8(2)); +console.log(power8(3)); +console.log(power8(4)); + +const a = 10; +function mostOuter() { + function outer() { + console.log(a); + } +} + +{ + const notScoped = 'scoped'; + { + { + { + console.log(notScoped); + } + } + } +} + +function A(a) { + console.log('I am A'); + if (a >= 10) { + console.log('a = ', a); + } + for (let i = 0; i < a; i++) { + console.log(i); + } +} + +function B() { + A(5); +} + +function C() { + B(); + B(); +} +function D() { + C(); + A(3); +} + +D(); + +function randomSum(max) { + const random1 = Math.floor(Math.random() * max); + const random2 = Math.floor(Math.random() * max); + t(); + function t() { + console.log(test); + } + var test = 'something'; + t(); + return random1 + random2; // placeholder +} + +const r = randomSum(15); + +(function (name) { + console.log(name); +})('Nayem'); + +(() => { + console.log('Test'); +})(); diff --git a/full-stack-army/src/lecture-10/app.js b/full-stack-army/src/lecture-10/app.js new file mode 100644 index 0000000..22d3dfe --- /dev/null +++ b/full-stack-army/src/lecture-10/app.js @@ -0,0 +1,185 @@ +// console.log(1); + +// setTimeout(() => { +// console.log(2); +// }, 0); + +// setTimeout(() => { +// console.log(3); +// }, 0); + +// setTimeout(() => { +// console.log(4); +// }, 0); + +// setTimeout(() => { +// console.log(5); +// }, 0); + +// setTimeout(() => { +// console.log(6); +// }, 0); + +// setTimeout(() => { +// console.log(7); +// }, 0); + +// console.log(8); + +// function main() { +// setTimeout(() => { +// console.log('load last'); +// }, 10); + +// setTimeout(() => { +// console.log('load first'); +// test(); +// }, 0); + +// test(); +// } + +// function test() { +// console.log('test'); +// } + +// main(); + +// Callback +/** + * 1. find user by username + * 2. find post by userId + * 3. find latest post + * 4. find comments by post id + * 5. find latest comment + * 6. find username of the latest commented user + */ + +/** + * /users?username=[username] + * /posts?user_id=[user_id] + * /comments?post_id=[post_id] + * /users?username=[username] + */ + +// function get(path, cb) { +// const data = {}; // somehow process it +// cb(data); +// } + +// function getUserNameFromComment(username) { +// get(`users?username=${username}`, (user) => { +// get(`posts?user_id=${user.id}`, (posts) => { +// get(`comments?post_id=${posts[0].id}`, (comments) => { +// get(`users?username=${comments[0].username}`, (user) => { +// console.log(user); +// }); +// }); +// }); +// }); +// } + +// getUserNameFromComment('arif'); + +/* const isResolved = true; + +const promise = new Promise((resolve, reject) => { + if (isResolved) { + resolve('completed'); + } else { + reject('data'); + } +}); + +console.log(promise); + +promise + .then((result) => { + console.log(result); + }) + .catch((e) => { + console.log('Rejected'); + }); */ + +// function wait(ms) { +// const promise = new Promise((resolve) => { +// setTimeout(resolve, ms); +// }); +// return promise; +// } + +// const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + +// wait(1000).then(() => { +// console.log('Done in 1000ms'); +// }); + +// wait(2000).then(() => { +// console.log('Done in 2000ms'); +// }); + +// wait(3000).then(() => { +// console.log('Done in 3000ms'); +// }); + +// const get = (url) => Promise.resolve(); + +// get(`/users?username=anarul`) +// .then((user) => { +// /** do all other operations here */ +// return get(`/posts?user_id=${user.id}`); +// }) +// .then((posts) => { +// const latestPost = posts[0]; +// return get(`/comments?post_id=${latestPost.id}`); +// }) +// .then((comments) => { +// const latestComment = comments[0]; +// return get(`/users?username=${latestComment.username}`); +// }) +// .then((user) => { +// console.log(user); +// }) +// .catch(() => { +// console.log('Error'); +// }); + +// const get = (url) => Promise.resolve(); + +// async function getUserName(username) { +// try { +// const mainUser = await get(`/users?username=${username}`); +// const posts = await get(`/posts?user_id=${mainUser.id}`); +// const comments = await get(`/comments?post_id=${posts[0].id}`); +// const user = await get(`/users?username=${comments[0].username}`); +// console.log(user); +// } catch (e) { +// console.log(e); +// } +// } + +const axios = require('axios').default; +const USERS_URL = 'https://jsonplaceholder.typicode.com/users'; +const POSTS_URL = 'https://jsonplaceholder.typicode.com/posts'; +const COMMENTS_URL = 'https://jsonplaceholder.typicode.com/comments'; + +async function getComments(username) { + try { + const { data: user } = await axios.get(`${USERS_URL}?username=${username}`); + const { data: posts } = await axios.get( + `${POSTS_URL}?userId=${user[0].id}` + ); + const { data: comments } = await axios.get( + `${COMMENTS_URL}?postId=${posts[0].id}` + ); + + const { data: userWithComment } = await axios.get( + `${USERS_URL}?email=${comments[1].email}` + ); + console.log(userWithComment); + } catch (error) { + console.log('Error Occurred', error.toJSON()); + } +} + +getComments('Bret'); diff --git a/full-stack-army/src/lecture-10/package.json b/full-stack-army/src/lecture-10/package.json new file mode 100644 index 0000000..9725a93 --- /dev/null +++ b/full-stack-army/src/lecture-10/package.json @@ -0,0 +1,15 @@ +{ + "name": "lecture-10", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^0.26.1" + } +} diff --git a/src/lecture-10/yarn.lock b/full-stack-army/src/lecture-10/yarn.lock similarity index 100% rename from src/lecture-10/yarn.lock rename to full-stack-army/src/lecture-10/yarn.lock diff --git a/full-stack-army/src/lecture-11/app.js b/full-stack-army/src/lecture-11/app.js new file mode 100644 index 0000000..ff349fd --- /dev/null +++ b/full-stack-army/src/lecture-11/app.js @@ -0,0 +1,156 @@ +// const arr = [1, 2, 3, 4]; +// let index = 0; +// function next() { +// return arr[index++]; +// } + +// console.log(next()); +// console.log(next()); + +// const channel = 'Stack'; +// const channelIterator = channel[Symbol.iterator](); +// console.log(channelIterator.next()); +// console.log(channelIterator.next()); +// console.log(channelIterator.next()); +// console.log(channelIterator.next()); +// console.log(channelIterator.next()); +// console.log(channelIterator.next()); +// console.log(channelIterator.next()); + +// for (let v of channel) { +// console.log(v); +// } + +// while (true) { +// const data = channelIterator.next(); +// if (data.done) { +// break; +// } +// console.log(data.value); +// } + +// const range = { +// start: 0, +// stop: 100, +// step: 5, +// }; +// range[Symbol.iterator] = function () { +// let current = this.start; +// const stop = this.stop; +// const step = this.step; +// return { +// next() { +// const o = { +// value: current, +// done: current > stop, +// }; +// current += step; +// return o; +// }, +// }; +// }; + +// for (let v of range) { +// console.log(v); +// } + +// const rangeIterator = range[Symbol.iterator](); +// console.log(rangeIterator.next()); +// console.log(rangeIterator.next()); +// console.log(rangeIterator.next()); + +// function* myGenerator() { +// yield 1; +// yield 2; +// yield 3; +// } + +// const iterator = myGenerator(); +// console.log(iterator.next()); +// console.log(iterator.next()); +// console.log(iterator.next()); +// console.log(iterator.next()); + +// Generator always returns iterator + +// function* range(start = 0, stop = 100, step = 5) { +// for (let i = start; i <= stop; i += step) { +// yield i; +// } +// } + +// const rangeIt = range(1, 10, 3); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); +// console.log(rangeIt.next()); + +// for (let v of range()) { +// console.log(v); +// } + +// function* generateId() { +// let index = 1; +// while (true) { +// yield index++; +// } +// } + +// const generateUserId = generateId(); +// const generateProductId = generateId(); +// console.log('User', generateUserId.next().value); +// console.log('User', generateUserId.next().value); +// console.log('User', generateUserId.next().value); + +// console.log('Product', generateProductId.next().value); +// console.log('Product', generateProductId.next().value); +// console.log('Product', generateProductId.next().value); +// console.log('Product', generateProductId.next().value); +// console.log('Product', generateProductId.next().value); +// console.log('Product', generateProductId.next().value); +const axios = require('axios').default; + +async function getUsers() { + const url = 'https://jsonplaceholder.typicode.com/users'; + const { data: users } = await axios.get(url); + return users.map((user) => + axios.get(`https://jsonplaceholder.typicode.com/posts?userId=${user.id}`) + ); +} + +// async function* getPostsByUser(users) { +// const url = 'https://jsonplaceholder.typicode.com/posts'; +// for (let i = 0; i < users.length; i++) { +// const { data: posts } = await axios.get(`${url}?userId=${users[i].id}`); +// yield posts; +// } +// } + +// getUsers() +// .then(async (users) => { +// // const userIterator = await getPostsByUser(users); +// // await userIterator.next(); +// // await userIterator.next(); +// // console.log((await userIterator.next()).value); + +// // for await (let v of getPostsByUser(users)) { +// // console.log(v.map((d) => d.title)); +// // } + +// console.log(users); +// }) +// .catch((e) => { +// console.log(e); +// }); + +(async () => { + const users = await getUsers(); + for await (let v of users) { + console.log(v.data.map((post) => post.title)); + } +})(); diff --git a/full-stack-army/src/lecture-11/package.json b/full-stack-army/src/lecture-11/package.json new file mode 100644 index 0000000..f796e28 --- /dev/null +++ b/full-stack-army/src/lecture-11/package.json @@ -0,0 +1,15 @@ +{ + "name": "lecture-11", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^0.27.2" + } +} diff --git a/src/lecture-11/yarn.lock b/full-stack-army/src/lecture-11/yarn.lock similarity index 100% rename from src/lecture-11/yarn.lock rename to full-stack-army/src/lecture-11/yarn.lock diff --git a/src/lecture-22/attendance-system/server/models/AdminAttendance.js b/full-stack-army/src/lecture-13/attendance-system/server/models/AdminAttendance.js similarity index 100% rename from src/lecture-22/attendance-system/server/models/AdminAttendance.js rename to full-stack-army/src/lecture-13/attendance-system/server/models/AdminAttendance.js diff --git a/src/lecture-22/attendance-system/server/models/Profile.js b/full-stack-army/src/lecture-13/attendance-system/server/models/Profile.js similarity index 100% rename from src/lecture-22/attendance-system/server/models/Profile.js rename to full-stack-army/src/lecture-13/attendance-system/server/models/Profile.js diff --git a/src/lecture-22/attendance-system/server/models/StudentAttendance.js b/full-stack-army/src/lecture-13/attendance-system/server/models/StudentAttendance.js similarity index 100% rename from src/lecture-22/attendance-system/server/models/StudentAttendance.js rename to full-stack-army/src/lecture-13/attendance-system/server/models/StudentAttendance.js diff --git a/full-stack-army/src/lecture-13/attendance-system/server/models/User.js b/full-stack-army/src/lecture-13/attendance-system/server/models/User.js new file mode 100644 index 0000000..149e9e9 --- /dev/null +++ b/full-stack-army/src/lecture-13/attendance-system/server/models/User.js @@ -0,0 +1,13 @@ +const { model, Schema } = require('mongoose'); + +const userSchema = new Schema({ + name: String, + email: String, + password: String, + roles: [String], + accountStatus: String, +}); + +const User = model('User', userSchema); + +module.exports = User; diff --git a/full-stack-army/src/lecture-13/attendance-system/server/package.json b/full-stack-army/src/lecture-13/attendance-system/server/package.json new file mode 100644 index 0000000..52f5669 --- /dev/null +++ b/full-stack-army/src/lecture-13/attendance-system/server/package.json @@ -0,0 +1,20 @@ +{ + "name": "server", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "nodemon server.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "express": "^4.18.1", + "mongoose": "^6.3.3" + }, + "devDependencies": { + "nodemon": "^2.0.16" + } +} diff --git a/full-stack-army/src/lecture-13/attendance-system/server/server.js b/full-stack-army/src/lecture-13/attendance-system/server/server.js new file mode 100644 index 0000000..fb40f2d --- /dev/null +++ b/full-stack-army/src/lecture-13/attendance-system/server/server.js @@ -0,0 +1,14 @@ +const express = require('express'); +const app = express(); + +app.get('/', (req, res) => { + const obj = { + name: 'Ayman', + email: 'ayman@example.com', + }; + res.json(obj); +}); + +app.listen(4000, () => { + console.log("I'm listening on port 4000"); +}); diff --git a/src/lecture-15/package.json b/full-stack-army/src/lecture-15/package.json similarity index 100% rename from src/lecture-15/package.json rename to full-stack-army/src/lecture-15/package.json diff --git a/src/lecture-15/server.js b/full-stack-army/src/lecture-15/server.js similarity index 100% rename from src/lecture-15/server.js rename to full-stack-army/src/lecture-15/server.js diff --git a/src/lecture-15/yarn.lock b/full-stack-army/src/lecture-15/yarn.lock similarity index 100% rename from src/lecture-15/yarn.lock rename to full-stack-army/src/lecture-15/yarn.lock diff --git a/src/lecture-16/app/app.js b/full-stack-army/src/lecture-16/app/app.js similarity index 100% rename from src/lecture-16/app/app.js rename to full-stack-army/src/lecture-16/app/app.js diff --git a/src/lecture-16/app/error.js b/full-stack-army/src/lecture-16/app/error.js similarity index 100% rename from src/lecture-16/app/error.js rename to full-stack-army/src/lecture-16/app/error.js diff --git a/src/lecture-16/app/middleware.js b/full-stack-army/src/lecture-16/app/middleware.js similarity index 100% rename from src/lecture-16/app/middleware.js rename to full-stack-army/src/lecture-16/app/middleware.js diff --git a/src/lecture-16/app/routes.js b/full-stack-army/src/lecture-16/app/routes.js similarity index 100% rename from src/lecture-16/app/routes.js rename to full-stack-army/src/lecture-16/app/routes.js diff --git a/src/lecture-16/default.env b/full-stack-army/src/lecture-16/default.env similarity index 100% rename from src/lecture-16/default.env rename to full-stack-army/src/lecture-16/default.env diff --git a/src/lecture-16/package.json b/full-stack-army/src/lecture-16/package.json similarity index 100% rename from src/lecture-16/package.json rename to full-stack-army/src/lecture-16/package.json diff --git a/src/lecture-16/public/index.html b/full-stack-army/src/lecture-16/public/index.html similarity index 100% rename from src/lecture-16/public/index.html rename to full-stack-army/src/lecture-16/public/index.html diff --git a/src/lecture-16/server.js b/full-stack-army/src/lecture-16/server.js similarity index 100% rename from src/lecture-16/server.js rename to full-stack-army/src/lecture-16/server.js diff --git a/src/lecture-16/yarn.lock b/full-stack-army/src/lecture-16/yarn.lock similarity index 100% rename from src/lecture-16/yarn.lock rename to full-stack-army/src/lecture-16/yarn.lock diff --git a/src/lecture-22/attendance-system/server/db.js b/full-stack-army/src/lecture-22/attendance-system/server/db.js similarity index 100% rename from src/lecture-22/attendance-system/server/db.js rename to full-stack-army/src/lecture-22/attendance-system/server/db.js diff --git a/src/lecture-23/attendance-system/server/models/AdminAttendance.js b/full-stack-army/src/lecture-22/attendance-system/server/models/AdminAttendance.js similarity index 100% rename from src/lecture-23/attendance-system/server/models/AdminAttendance.js rename to full-stack-army/src/lecture-22/attendance-system/server/models/AdminAttendance.js diff --git a/src/lecture-23/attendance-system/server/models/Profile.js b/full-stack-army/src/lecture-22/attendance-system/server/models/Profile.js similarity index 100% rename from src/lecture-23/attendance-system/server/models/Profile.js rename to full-stack-army/src/lecture-22/attendance-system/server/models/Profile.js diff --git a/src/lecture-23/attendance-system/server/models/StudentAttendance.js b/full-stack-army/src/lecture-22/attendance-system/server/models/StudentAttendance.js similarity index 100% rename from src/lecture-23/attendance-system/server/models/StudentAttendance.js rename to full-stack-army/src/lecture-22/attendance-system/server/models/StudentAttendance.js diff --git a/src/lecture-22/attendance-system/server/models/User.js b/full-stack-army/src/lecture-22/attendance-system/server/models/User.js similarity index 100% rename from src/lecture-22/attendance-system/server/models/User.js rename to full-stack-army/src/lecture-22/attendance-system/server/models/User.js diff --git a/src/lecture-22/attendance-system/server/package.json b/full-stack-army/src/lecture-22/attendance-system/server/package.json similarity index 100% rename from src/lecture-22/attendance-system/server/package.json rename to full-stack-army/src/lecture-22/attendance-system/server/package.json diff --git a/src/lecture-22/attendance-system/server/server.js b/full-stack-army/src/lecture-22/attendance-system/server/server.js similarity index 100% rename from src/lecture-22/attendance-system/server/server.js rename to full-stack-army/src/lecture-22/attendance-system/server/server.js diff --git a/src/lecture-22/attendance-system/server/yarn.lock b/full-stack-army/src/lecture-22/attendance-system/server/yarn.lock similarity index 100% rename from src/lecture-22/attendance-system/server/yarn.lock rename to full-stack-army/src/lecture-22/attendance-system/server/yarn.lock diff --git a/src/lecture-23/attendance-system/server/controller/auth.js b/full-stack-army/src/lecture-23/attendance-system/server/controller/auth.js similarity index 100% rename from src/lecture-23/attendance-system/server/controller/auth.js rename to full-stack-army/src/lecture-23/attendance-system/server/controller/auth.js diff --git a/src/lecture-23/attendance-system/server/db.js b/full-stack-army/src/lecture-23/attendance-system/server/db.js similarity index 100% rename from src/lecture-23/attendance-system/server/db.js rename to full-stack-army/src/lecture-23/attendance-system/server/db.js diff --git a/src/lecture-23/attendance-system/server/middleware/authenticate.js b/full-stack-army/src/lecture-23/attendance-system/server/middleware/authenticate.js similarity index 100% rename from src/lecture-23/attendance-system/server/middleware/authenticate.js rename to full-stack-army/src/lecture-23/attendance-system/server/middleware/authenticate.js diff --git a/src/lecture-24/attendance-system/server/models/AdminAttendance.js b/full-stack-army/src/lecture-23/attendance-system/server/models/AdminAttendance.js similarity index 100% rename from src/lecture-24/attendance-system/server/models/AdminAttendance.js rename to full-stack-army/src/lecture-23/attendance-system/server/models/AdminAttendance.js diff --git a/src/lecture-24/attendance-system/server/models/Profile.js b/full-stack-army/src/lecture-23/attendance-system/server/models/Profile.js similarity index 100% rename from src/lecture-24/attendance-system/server/models/Profile.js rename to full-stack-army/src/lecture-23/attendance-system/server/models/Profile.js diff --git a/src/lecture-24/attendance-system/server/models/StudentAttendance.js b/full-stack-army/src/lecture-23/attendance-system/server/models/StudentAttendance.js similarity index 100% rename from src/lecture-24/attendance-system/server/models/StudentAttendance.js rename to full-stack-army/src/lecture-23/attendance-system/server/models/StudentAttendance.js diff --git a/src/lecture-23/attendance-system/server/models/User.js b/full-stack-army/src/lecture-23/attendance-system/server/models/User.js similarity index 100% rename from src/lecture-23/attendance-system/server/models/User.js rename to full-stack-army/src/lecture-23/attendance-system/server/models/User.js diff --git a/src/lecture-23/attendance-system/server/package.json b/full-stack-army/src/lecture-23/attendance-system/server/package.json similarity index 100% rename from src/lecture-23/attendance-system/server/package.json rename to full-stack-army/src/lecture-23/attendance-system/server/package.json diff --git a/src/lecture-23/attendance-system/server/routes/auth.js b/full-stack-army/src/lecture-23/attendance-system/server/routes/auth.js similarity index 100% rename from src/lecture-23/attendance-system/server/routes/auth.js rename to full-stack-army/src/lecture-23/attendance-system/server/routes/auth.js diff --git a/src/lecture-23/attendance-system/server/routes/index.js b/full-stack-army/src/lecture-23/attendance-system/server/routes/index.js similarity index 100% rename from src/lecture-23/attendance-system/server/routes/index.js rename to full-stack-army/src/lecture-23/attendance-system/server/routes/index.js diff --git a/src/lecture-23/attendance-system/server/server.js b/full-stack-army/src/lecture-23/attendance-system/server/server.js similarity index 100% rename from src/lecture-23/attendance-system/server/server.js rename to full-stack-army/src/lecture-23/attendance-system/server/server.js diff --git a/src/lecture-23/attendance-system/server/service/auth.js b/full-stack-army/src/lecture-23/attendance-system/server/service/auth.js similarity index 100% rename from src/lecture-23/attendance-system/server/service/auth.js rename to full-stack-army/src/lecture-23/attendance-system/server/service/auth.js diff --git a/src/lecture-23/attendance-system/server/service/user.js b/full-stack-army/src/lecture-23/attendance-system/server/service/user.js similarity index 100% rename from src/lecture-23/attendance-system/server/service/user.js rename to full-stack-army/src/lecture-23/attendance-system/server/service/user.js diff --git a/src/lecture-23/attendance-system/server/utils/error.js b/full-stack-army/src/lecture-23/attendance-system/server/utils/error.js similarity index 100% rename from src/lecture-23/attendance-system/server/utils/error.js rename to full-stack-army/src/lecture-23/attendance-system/server/utils/error.js diff --git a/src/lecture-23/attendance-system/server/yarn.lock b/full-stack-army/src/lecture-23/attendance-system/server/yarn.lock similarity index 100% rename from src/lecture-23/attendance-system/server/yarn.lock rename to full-stack-army/src/lecture-23/attendance-system/server/yarn.lock diff --git a/src/lecture-24/attendance-system/server/controller/auth.js b/full-stack-army/src/lecture-24/attendance-system/server/controller/auth.js similarity index 100% rename from src/lecture-24/attendance-system/server/controller/auth.js rename to full-stack-army/src/lecture-24/attendance-system/server/controller/auth.js diff --git a/src/lecture-24/attendance-system/server/controller/users.js b/full-stack-army/src/lecture-24/attendance-system/server/controller/users.js similarity index 100% rename from src/lecture-24/attendance-system/server/controller/users.js rename to full-stack-army/src/lecture-24/attendance-system/server/controller/users.js diff --git a/src/lecture-24/attendance-system/server/db.js b/full-stack-army/src/lecture-24/attendance-system/server/db.js similarity index 100% rename from src/lecture-24/attendance-system/server/db.js rename to full-stack-army/src/lecture-24/attendance-system/server/db.js diff --git a/src/lecture-24/attendance-system/server/middleware/authenticate.js b/full-stack-army/src/lecture-24/attendance-system/server/middleware/authenticate.js similarity index 100% rename from src/lecture-24/attendance-system/server/middleware/authenticate.js rename to full-stack-army/src/lecture-24/attendance-system/server/middleware/authenticate.js diff --git a/full-stack-army/src/lecture-24/attendance-system/server/models/AdminAttendance.js b/full-stack-army/src/lecture-24/attendance-system/server/models/AdminAttendance.js new file mode 100644 index 0000000..07ca223 --- /dev/null +++ b/full-stack-army/src/lecture-24/attendance-system/server/models/AdminAttendance.js @@ -0,0 +1,11 @@ +const { Schema, model } = require('mongoose'); + +const adminAttendanceSchema = new Schema({ + timeLimit: Number, + status: String, + createdAt: Date, +}); + +const AdminAttendance = model('AdminAttendance', adminAttendanceSchema); + +module.exports = AdminAttendance; diff --git a/src/lecture-26/attendance-system/server/models/Profile.js b/full-stack-army/src/lecture-24/attendance-system/server/models/Profile.js similarity index 100% rename from src/lecture-26/attendance-system/server/models/Profile.js rename to full-stack-army/src/lecture-24/attendance-system/server/models/Profile.js diff --git a/full-stack-army/src/lecture-24/attendance-system/server/models/StudentAttendance.js b/full-stack-army/src/lecture-24/attendance-system/server/models/StudentAttendance.js new file mode 100644 index 0000000..db594ea --- /dev/null +++ b/full-stack-army/src/lecture-24/attendance-system/server/models/StudentAttendance.js @@ -0,0 +1,17 @@ +const { Schema, model } = require('mongoose'); + +const studentAttendanceSchema = new Schema({ + createdAt: Date, + user: { + type: Schema.Types.ObjectId, + ref: 'User', + }, + adminAttendance: { + type: Schema.Types.ObjectId, + ref: 'AdminAttendance', + }, +}); + +const StudentAttendance = model('StudentAttendance', studentAttendanceSchema); + +module.exports = StudentAttendance; diff --git a/src/lecture-24/attendance-system/server/models/User.js b/full-stack-army/src/lecture-24/attendance-system/server/models/User.js similarity index 100% rename from src/lecture-24/attendance-system/server/models/User.js rename to full-stack-army/src/lecture-24/attendance-system/server/models/User.js diff --git a/src/lecture-24/attendance-system/server/package.json b/full-stack-army/src/lecture-24/attendance-system/server/package.json similarity index 100% rename from src/lecture-24/attendance-system/server/package.json rename to full-stack-army/src/lecture-24/attendance-system/server/package.json diff --git a/src/lecture-24/attendance-system/server/routes/auth.js b/full-stack-army/src/lecture-24/attendance-system/server/routes/auth.js similarity index 100% rename from src/lecture-24/attendance-system/server/routes/auth.js rename to full-stack-army/src/lecture-24/attendance-system/server/routes/auth.js diff --git a/src/lecture-24/attendance-system/server/routes/index.js b/full-stack-army/src/lecture-24/attendance-system/server/routes/index.js similarity index 100% rename from src/lecture-24/attendance-system/server/routes/index.js rename to full-stack-army/src/lecture-24/attendance-system/server/routes/index.js diff --git a/src/lecture-24/attendance-system/server/routes/users.js b/full-stack-army/src/lecture-24/attendance-system/server/routes/users.js similarity index 100% rename from src/lecture-24/attendance-system/server/routes/users.js rename to full-stack-army/src/lecture-24/attendance-system/server/routes/users.js diff --git a/src/lecture-24/attendance-system/server/server.js b/full-stack-army/src/lecture-24/attendance-system/server/server.js similarity index 100% rename from src/lecture-24/attendance-system/server/server.js rename to full-stack-army/src/lecture-24/attendance-system/server/server.js diff --git a/src/lecture-24/attendance-system/server/service/auth.js b/full-stack-army/src/lecture-24/attendance-system/server/service/auth.js similarity index 100% rename from src/lecture-24/attendance-system/server/service/auth.js rename to full-stack-army/src/lecture-24/attendance-system/server/service/auth.js diff --git a/src/lecture-24/attendance-system/server/service/user.js b/full-stack-army/src/lecture-24/attendance-system/server/service/user.js similarity index 100% rename from src/lecture-24/attendance-system/server/service/user.js rename to full-stack-army/src/lecture-24/attendance-system/server/service/user.js diff --git a/src/lecture-24/attendance-system/server/utils/error.js b/full-stack-army/src/lecture-24/attendance-system/server/utils/error.js similarity index 100% rename from src/lecture-24/attendance-system/server/utils/error.js rename to full-stack-army/src/lecture-24/attendance-system/server/utils/error.js diff --git a/src/lecture-24/attendance-system/server/yarn.lock b/full-stack-army/src/lecture-24/attendance-system/server/yarn.lock similarity index 100% rename from src/lecture-24/attendance-system/server/yarn.lock rename to full-stack-army/src/lecture-24/attendance-system/server/yarn.lock diff --git a/src/lecture-26/attendance-system/server/controller/admin-attendance.js b/full-stack-army/src/lecture-26/attendance-system/server/controller/admin-attendance.js similarity index 100% rename from src/lecture-26/attendance-system/server/controller/admin-attendance.js rename to full-stack-army/src/lecture-26/attendance-system/server/controller/admin-attendance.js diff --git a/src/lecture-26/attendance-system/server/controller/auth.js b/full-stack-army/src/lecture-26/attendance-system/server/controller/auth.js similarity index 100% rename from src/lecture-26/attendance-system/server/controller/auth.js rename to full-stack-army/src/lecture-26/attendance-system/server/controller/auth.js diff --git a/src/lecture-26/attendance-system/server/controller/student-attendance.js b/full-stack-army/src/lecture-26/attendance-system/server/controller/student-attendance.js similarity index 100% rename from src/lecture-26/attendance-system/server/controller/student-attendance.js rename to full-stack-army/src/lecture-26/attendance-system/server/controller/student-attendance.js diff --git a/src/lecture-26/attendance-system/server/controller/users.js b/full-stack-army/src/lecture-26/attendance-system/server/controller/users.js similarity index 100% rename from src/lecture-26/attendance-system/server/controller/users.js rename to full-stack-army/src/lecture-26/attendance-system/server/controller/users.js diff --git a/src/lecture-26/attendance-system/server/db.js b/full-stack-army/src/lecture-26/attendance-system/server/db.js similarity index 100% rename from src/lecture-26/attendance-system/server/db.js rename to full-stack-army/src/lecture-26/attendance-system/server/db.js diff --git a/src/lecture-26/attendance-system/server/middleware/authenticate.js b/full-stack-army/src/lecture-26/attendance-system/server/middleware/authenticate.js similarity index 100% rename from src/lecture-26/attendance-system/server/middleware/authenticate.js rename to full-stack-army/src/lecture-26/attendance-system/server/middleware/authenticate.js diff --git a/src/lecture-26/attendance-system/server/models/AdminAttendance.js b/full-stack-army/src/lecture-26/attendance-system/server/models/AdminAttendance.js similarity index 100% rename from src/lecture-26/attendance-system/server/models/AdminAttendance.js rename to full-stack-army/src/lecture-26/attendance-system/server/models/AdminAttendance.js diff --git a/full-stack-army/src/lecture-26/attendance-system/server/models/Profile.js b/full-stack-army/src/lecture-26/attendance-system/server/models/Profile.js new file mode 100644 index 0000000..cfdcd66 --- /dev/null +++ b/full-stack-army/src/lecture-26/attendance-system/server/models/Profile.js @@ -0,0 +1,16 @@ +const { model, Schema } = require('mongoose'); + +const profileSchema = new Schema({ + firstName: String, + lastName: String, + phone: String, + avatar: String, + user: { + type: Schema.Types.ObjectId, + ref: 'User', + }, +}); + +const Profile = model('Profile', profileSchema); + +module.exports = Profile; diff --git a/src/lecture-26/attendance-system/server/models/StudentAttendance.js b/full-stack-army/src/lecture-26/attendance-system/server/models/StudentAttendance.js similarity index 100% rename from src/lecture-26/attendance-system/server/models/StudentAttendance.js rename to full-stack-army/src/lecture-26/attendance-system/server/models/StudentAttendance.js diff --git a/src/lecture-26/attendance-system/server/models/User.js b/full-stack-army/src/lecture-26/attendance-system/server/models/User.js similarity index 100% rename from src/lecture-26/attendance-system/server/models/User.js rename to full-stack-army/src/lecture-26/attendance-system/server/models/User.js diff --git a/src/lecture-26/attendance-system/server/package.json b/full-stack-army/src/lecture-26/attendance-system/server/package.json similarity index 100% rename from src/lecture-26/attendance-system/server/package.json rename to full-stack-army/src/lecture-26/attendance-system/server/package.json diff --git a/src/lecture-26/attendance-system/server/routes/admin-attendance.js b/full-stack-army/src/lecture-26/attendance-system/server/routes/admin-attendance.js similarity index 100% rename from src/lecture-26/attendance-system/server/routes/admin-attendance.js rename to full-stack-army/src/lecture-26/attendance-system/server/routes/admin-attendance.js diff --git a/src/lecture-26/attendance-system/server/routes/auth.js b/full-stack-army/src/lecture-26/attendance-system/server/routes/auth.js similarity index 100% rename from src/lecture-26/attendance-system/server/routes/auth.js rename to full-stack-army/src/lecture-26/attendance-system/server/routes/auth.js diff --git a/src/lecture-26/attendance-system/server/routes/index.js b/full-stack-army/src/lecture-26/attendance-system/server/routes/index.js similarity index 100% rename from src/lecture-26/attendance-system/server/routes/index.js rename to full-stack-army/src/lecture-26/attendance-system/server/routes/index.js diff --git a/src/lecture-26/attendance-system/server/routes/student-attendance.js b/full-stack-army/src/lecture-26/attendance-system/server/routes/student-attendance.js similarity index 100% rename from src/lecture-26/attendance-system/server/routes/student-attendance.js rename to full-stack-army/src/lecture-26/attendance-system/server/routes/student-attendance.js diff --git a/src/lecture-26/attendance-system/server/routes/users.js b/full-stack-army/src/lecture-26/attendance-system/server/routes/users.js similarity index 100% rename from src/lecture-26/attendance-system/server/routes/users.js rename to full-stack-army/src/lecture-26/attendance-system/server/routes/users.js diff --git a/src/lecture-26/attendance-system/server/server.js b/full-stack-army/src/lecture-26/attendance-system/server/server.js similarity index 100% rename from src/lecture-26/attendance-system/server/server.js rename to full-stack-army/src/lecture-26/attendance-system/server/server.js diff --git a/src/lecture-26/attendance-system/server/service/auth.js b/full-stack-army/src/lecture-26/attendance-system/server/service/auth.js similarity index 100% rename from src/lecture-26/attendance-system/server/service/auth.js rename to full-stack-army/src/lecture-26/attendance-system/server/service/auth.js diff --git a/src/lecture-26/attendance-system/server/service/user.js b/full-stack-army/src/lecture-26/attendance-system/server/service/user.js similarity index 100% rename from src/lecture-26/attendance-system/server/service/user.js rename to full-stack-army/src/lecture-26/attendance-system/server/service/user.js diff --git a/src/lecture-26/attendance-system/server/utils/error.js b/full-stack-army/src/lecture-26/attendance-system/server/utils/error.js similarity index 100% rename from src/lecture-26/attendance-system/server/utils/error.js rename to full-stack-army/src/lecture-26/attendance-system/server/utils/error.js diff --git a/src/lecture-26/attendance-system/server/yarn.lock b/full-stack-army/src/lecture-26/attendance-system/server/yarn.lock similarity index 100% rename from src/lecture-26/attendance-system/server/yarn.lock rename to full-stack-army/src/lecture-26/attendance-system/server/yarn.lock diff --git a/src/lecture-29/app.js b/full-stack-army/src/lecture-29/app.js similarity index 100% rename from src/lecture-29/app.js rename to full-stack-army/src/lecture-29/app.js diff --git a/src/lecture-29/index.html b/full-stack-army/src/lecture-29/index.html similarity index 100% rename from src/lecture-29/index.html rename to full-stack-army/src/lecture-29/index.html diff --git a/src/lecture-29/react-app/first-app/.gitignore b/full-stack-army/src/lecture-29/react-app/first-app/.gitignore similarity index 100% rename from src/lecture-29/react-app/first-app/.gitignore rename to full-stack-army/src/lecture-29/react-app/first-app/.gitignore diff --git a/src/lecture-29/react-app/first-app/README.md b/full-stack-army/src/lecture-29/react-app/first-app/README.md similarity index 100% rename from src/lecture-29/react-app/first-app/README.md rename to full-stack-army/src/lecture-29/react-app/first-app/README.md diff --git a/src/lecture-29/react-app/first-app/package.json b/full-stack-army/src/lecture-29/react-app/first-app/package.json similarity index 100% rename from src/lecture-29/react-app/first-app/package.json rename to full-stack-army/src/lecture-29/react-app/first-app/package.json diff --git a/src/lecture-29/react-app/first-app/public/favicon.ico b/full-stack-army/src/lecture-29/react-app/first-app/public/favicon.ico similarity index 100% rename from src/lecture-29/react-app/first-app/public/favicon.ico rename to full-stack-army/src/lecture-29/react-app/first-app/public/favicon.ico diff --git a/src/lecture-29/react-app/first-app/public/index.html b/full-stack-army/src/lecture-29/react-app/first-app/public/index.html similarity index 100% rename from src/lecture-29/react-app/first-app/public/index.html rename to full-stack-army/src/lecture-29/react-app/first-app/public/index.html diff --git a/src/lecture-29/react-app/first-app/public/logo192.png b/full-stack-army/src/lecture-29/react-app/first-app/public/logo192.png similarity index 100% rename from src/lecture-29/react-app/first-app/public/logo192.png rename to full-stack-army/src/lecture-29/react-app/first-app/public/logo192.png diff --git a/src/lecture-29/react-app/first-app/public/logo512.png b/full-stack-army/src/lecture-29/react-app/first-app/public/logo512.png similarity index 100% rename from src/lecture-29/react-app/first-app/public/logo512.png rename to full-stack-army/src/lecture-29/react-app/first-app/public/logo512.png diff --git a/src/lecture-29/react-app/first-app/public/manifest.json b/full-stack-army/src/lecture-29/react-app/first-app/public/manifest.json similarity index 100% rename from src/lecture-29/react-app/first-app/public/manifest.json rename to full-stack-army/src/lecture-29/react-app/first-app/public/manifest.json diff --git a/src/lecture-29/react-app/first-app/public/robots.txt b/full-stack-army/src/lecture-29/react-app/first-app/public/robots.txt similarity index 100% rename from src/lecture-29/react-app/first-app/public/robots.txt rename to full-stack-army/src/lecture-29/react-app/first-app/public/robots.txt diff --git a/src/lecture-29/react-app/first-app/src/App.css b/full-stack-army/src/lecture-29/react-app/first-app/src/App.css similarity index 100% rename from src/lecture-29/react-app/first-app/src/App.css rename to full-stack-army/src/lecture-29/react-app/first-app/src/App.css diff --git a/src/lecture-29/react-app/first-app/src/App.jsx b/full-stack-army/src/lecture-29/react-app/first-app/src/App.jsx similarity index 100% rename from src/lecture-29/react-app/first-app/src/App.jsx rename to full-stack-army/src/lecture-29/react-app/first-app/src/App.jsx diff --git a/src/lecture-29/react-app/first-app/src/index.css b/full-stack-army/src/lecture-29/react-app/first-app/src/index.css similarity index 100% rename from src/lecture-29/react-app/first-app/src/index.css rename to full-stack-army/src/lecture-29/react-app/first-app/src/index.css diff --git a/src/lecture-29/react-app/first-app/src/index.js b/full-stack-army/src/lecture-29/react-app/first-app/src/index.js similarity index 100% rename from src/lecture-29/react-app/first-app/src/index.js rename to full-stack-army/src/lecture-29/react-app/first-app/src/index.js diff --git a/src/lecture-29/react-app/first-app/src/logo.svg b/full-stack-army/src/lecture-29/react-app/first-app/src/logo.svg similarity index 100% rename from src/lecture-29/react-app/first-app/src/logo.svg rename to full-stack-army/src/lecture-29/react-app/first-app/src/logo.svg diff --git a/src/lecture-29/react-app/first-app/src/reportWebVitals.js b/full-stack-army/src/lecture-29/react-app/first-app/src/reportWebVitals.js similarity index 100% rename from src/lecture-29/react-app/first-app/src/reportWebVitals.js rename to full-stack-army/src/lecture-29/react-app/first-app/src/reportWebVitals.js diff --git a/src/lecture-29/react-app/first-app/src/setupTests.js b/full-stack-army/src/lecture-29/react-app/first-app/src/setupTests.js similarity index 100% rename from src/lecture-29/react-app/first-app/src/setupTests.js rename to full-stack-army/src/lecture-29/react-app/first-app/src/setupTests.js diff --git a/src/lecture-29/react-app/first-app/yarn.lock b/full-stack-army/src/lecture-29/react-app/first-app/yarn.lock similarity index 100% rename from src/lecture-29/react-app/first-app/yarn.lock rename to full-stack-army/src/lecture-29/react-app/first-app/yarn.lock diff --git a/src/mongo-demo/index.js b/full-stack-army/src/mongo-demo/index.js similarity index 100% rename from src/mongo-demo/index.js rename to full-stack-army/src/mongo-demo/index.js diff --git a/src/mongo-demo/package.json b/full-stack-army/src/mongo-demo/package.json similarity index 100% rename from src/mongo-demo/package.json rename to full-stack-army/src/mongo-demo/package.json diff --git a/src/mongo-demo/yarn.lock b/full-stack-army/src/mongo-demo/yarn.lock similarity index 100% rename from src/mongo-demo/yarn.lock rename to full-stack-army/src/mongo-demo/yarn.lock diff --git a/src/raffle-draw/app/app.js b/full-stack-army/src/raffle-draw/app/app.js similarity index 100% rename from src/raffle-draw/app/app.js rename to full-stack-army/src/raffle-draw/app/app.js diff --git a/src/raffle-draw/app/error.js b/full-stack-army/src/raffle-draw/app/error.js similarity index 100% rename from src/raffle-draw/app/error.js rename to full-stack-army/src/raffle-draw/app/error.js diff --git a/src/raffle-draw/app/middleware.js b/full-stack-army/src/raffle-draw/app/middleware.js similarity index 100% rename from src/raffle-draw/app/middleware.js rename to full-stack-army/src/raffle-draw/app/middleware.js diff --git a/src/raffle-draw/app/routes.js b/full-stack-army/src/raffle-draw/app/routes.js similarity index 100% rename from src/raffle-draw/app/routes.js rename to full-stack-army/src/raffle-draw/app/routes.js diff --git a/src/raffle-draw/db/db.js b/full-stack-army/src/raffle-draw/db/db.js similarity index 100% rename from src/raffle-draw/db/db.js rename to full-stack-army/src/raffle-draw/db/db.js diff --git a/src/raffle-draw/default.env b/full-stack-army/src/raffle-draw/default.env similarity index 100% rename from src/raffle-draw/default.env rename to full-stack-army/src/raffle-draw/default.env diff --git a/src/raffle-draw/models/Ticket.js b/full-stack-army/src/raffle-draw/models/Ticket.js similarity index 100% rename from src/raffle-draw/models/Ticket.js rename to full-stack-army/src/raffle-draw/models/Ticket.js diff --git a/src/raffle-draw/package.json b/full-stack-army/src/raffle-draw/package.json similarity index 100% rename from src/raffle-draw/package.json rename to full-stack-army/src/raffle-draw/package.json diff --git a/src/raffle-draw/requirements.md b/full-stack-army/src/raffle-draw/requirements.md similarity index 100% rename from src/raffle-draw/requirements.md rename to full-stack-army/src/raffle-draw/requirements.md diff --git a/src/raffle-draw/routes/ticket.js b/full-stack-army/src/raffle-draw/routes/ticket.js similarity index 100% rename from src/raffle-draw/routes/ticket.js rename to full-stack-army/src/raffle-draw/routes/ticket.js diff --git a/src/raffle-draw/server.js b/full-stack-army/src/raffle-draw/server.js similarity index 100% rename from src/raffle-draw/server.js rename to full-stack-army/src/raffle-draw/server.js diff --git a/src/raffle-draw/test/test.js b/full-stack-army/src/raffle-draw/test/test.js similarity index 100% rename from src/raffle-draw/test/test.js rename to full-stack-army/src/raffle-draw/test/test.js diff --git a/src/raffle-draw/yarn.lock b/full-stack-army/src/raffle-draw/yarn.lock similarity index 100% rename from src/raffle-draw/yarn.lock rename to full-stack-army/src/raffle-draw/yarn.lock diff --git a/timestamp/Class - 7.md b/full-stack-army/timestamp/Class - 7.md similarity index 100% rename from timestamp/Class - 7.md rename to full-stack-army/timestamp/Class - 7.md diff --git a/timestamp/Class - 8.md b/full-stack-army/timestamp/Class - 8.md similarity index 100% rename from timestamp/Class - 8.md rename to full-stack-army/timestamp/Class - 8.md diff --git a/timestamp/Class - 9.md b/full-stack-army/timestamp/Class - 9.md similarity index 100% rename from timestamp/Class - 9.md rename to full-stack-army/timestamp/Class - 9.md diff --git a/timestamp/Class 10.md b/full-stack-army/timestamp/Class 10.md similarity index 100% rename from timestamp/Class 10.md rename to full-stack-army/timestamp/Class 10.md diff --git a/timestamp/class - 0.md b/full-stack-army/timestamp/class - 0.md similarity index 100% rename from timestamp/class - 0.md rename to full-stack-army/timestamp/class - 0.md diff --git a/timestamp/class - 1.md b/full-stack-army/timestamp/class - 1.md similarity index 100% rename from timestamp/class - 1.md rename to full-stack-army/timestamp/class - 1.md diff --git a/timestamp/class - 4.md b/full-stack-army/timestamp/class - 4.md similarity index 100% rename from timestamp/class - 4.md rename to full-stack-army/timestamp/class - 4.md diff --git a/timestamp/class - 5.md b/full-stack-army/timestamp/class - 5.md similarity index 100% rename from timestamp/class - 5.md rename to full-stack-army/timestamp/class - 5.md diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..eca821b --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,267 @@ +# Project information +site_name: Full Stack Army +site_url: https://fullstackarmy.taraldinn.me/ +site_author: Stack Learner +site_description: >- + Create a branded static site from a set of Markdown files to host the + documentation of your Open Source or commercial project +# Repository + + +repo_name: taraldinn/full-stack-army +repo_url: https://github.com/taraldinn/full-stack-army +edit_uri: full-stack-army/edit/master/docs + + + +copyright: Copyright © 2022 Stack Learner + + +extra_css: + - stylesheets/extra.css + + + +theme: + name: material + docs_dir: docs + # Static files + static_templates: + - 404.html + search_index_only: true + # Default values, taken from mkdocs_theme.yml + language: en + features: + - search.highlight + - search.share + - search.suggest + - content.code.annotate + - content.tabs.link + - content.tooltips + - header.autohide + - navigation.indexes + - navigation.instant + - navigation.prune + - navigation.sections + - navigation.tabs + - navigation.tabs.sticky + - navigation.top + - navigation.tracking + - search.highlight + - search.share + - search.suggest + - toc.follow + - toc.integrate + + + icon: + repo: fontawesome/brands/git-alt + logo: logo + palette: + - scheme: default + primary: blue + accent: pink + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - scheme: slate + primary: black + accent: pink + toggle: + icon: material/brightness-4 + name: Switch to light mode + font: + text: Hind Siliguri + code: Fira Code + favicon: assets/favicon.png + + +plugins: + - search + - minify: + minify_html: true + +extra: + + social: + - icon: fontawesome/brands/github + link: https://github.com/taraldinn + - icon: fontawesome/brands/gitter + link: https://gitter.im/squidfunk/mkdocs-material + - icon: fontawesome/brands/docker + link: https://hub.docker.com/r/squidfunk/mkdocs-material/ + - icon: fontawesome/brands/python + link: https://pypi.org/project/mkdocs-material/ + - icon: fontawesome/brands/twitter + link: https://twitter.com/taraldinn + + +markdown_extensions: + - abbr + - admonition + - attr_list + - def_list + - footnotes + - meta + - md_in_html + - toc: + permalink: true + - pymdownx.arithmatex: + generic: true + - pymdownx.betterem: + smart_enable: all + - pymdownx.caret + - pymdownx.details + - pymdownx.emoji: + emoji_generator: !!python/name:materialx.emoji.to_svg + emoji_index: !!python/name:materialx.emoji.twemoji + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.inlinehilite + - pymdownx.keys + - pymdownx.magiclink: + repo_url_shorthand: true + user: squidfunk + repo: mkdocs-material + - pymdownx.mark + - pymdownx.smartsymbols + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.tabbed: + alternate_style: true + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.tilde + + + +nav: + - Home: + - index.md + - references.md + - ClassLink.md + - Getting started: + # Nested every section Fundamental, Frontend, Backend, Authentication + - Fundamental: + - Welcome Decision Making: + - Overview: Lectures/Fundamentals/01/Overview.md + - Resource: Lectures/Fundamentals/01/resource.md + + - We Need Freedom, We have to Stop Technology War : + - Overview: Lectures/Fundamentals/02/Overview.md + - Resource: Lectures/Fundamentals/02/resource.md + + - Programming Language Foundation - A Bigger Landscape: + - Overview: Lectures/Fundamentals/03/Overview.md + - Resource: Lectures/Fundamentals/03/resource.md + + - Programming Fundamentals using JavaScript: + - Overview: Lectures/Fundamentals/04/Overview.md + - Resource: Lectures/Fundamentals/04/resource.md + + - Array Operations - Imperative vs Declarative: + - Overview: Lectures/Fundamentals/05/Overview.md + - Resource: Lectures/Fundamentals/05/resource.md + + - JavaScript Array and Object Deep Dive: + - Overview: Lectures/Fundamentals/06/Overview.md + - Resource: Lectures/Fundamentals/06/resource.md + + - QNA 1 - Don't Miss The Last Part: + - Overview: Lectures/Fundamentals/07/Overview.md + - Resource: Lectures/Fundamentals/07/resource.md + + - Understand JavaScript Functions: + - Overview: Lectures/Fundamentals/08/Overview.md + - Resource: Lectures/Fundamentals/08/resource.md + + - Functional Programming in JavaScript: + - Overview: Lectures/Fundamentals/09/Overview.md + - Resource: Lectures/Fundamentals/09/resource.md + + - Asynchronous Programming in JavaScript: + - Overview: Lectures/Fundamentals/10/Overview.md + - Resource: Lectures/Fundamentals/10/resource.md + + - Async Iterator & Generator in JavaScript: + - Overview: Lectures/Fundamentals/11/Overview.md + - Resource: Lectures/Fundamentals/11/resource.md + + - Attendance System Requirement Analysis: + - Overview: Lectures/Fundamentals/12/Overview.md + - Resource: Lectures/Fundamentals/12/resource.md + + - Create Models, Write Pseudo Code and Adda: + - Overview: Lectures/Fundamentals/13/Overview.md + - Resource: Lectures/Fundamentals/13/resource.md + + - Backend: + - Course Planning and Discussion: + - Overview: Lectures/Backend/14/Overview.md + - Resource: Lectures/Backend/14/resource.md + - Introduction to Backend Development: + - Overview: Lectures/Backend/15/Overview.md + - Resource: Lectures/Backend/15/resource.md + - Understand Express Middleware: + - Overview: Lectures/Backend/16/Overview.md + - Resource: Lectures/Backend/16/resource.md + - Raffle Draw Project: + - Overview: Lectures/Backend/17/Overview.md + - Resource: Lectures/Backend/17/resource.md + - Understand The Concepts of Database: + - Overview: Lectures/Backend/18/Overview.md + - Resource: Lectures/Backend/18/resource.md + - Adda with Random Topics: + - Overview: Lectures/Backend/19/Overview.md + - Resource: Lectures/Backend/19/resource.md + - Start Working with Mongoose: + - Overview: Lectures/Backend/20/Overview.md + - Resource: Lectures/Backend/20/resource.md + - QNA on Express 101 and Books: + - Overview: Lectures/Backend/21/Overview.md + # - Resource: Lectures/Backend/21/resource.md + - Authentication: + - Authentication System from Pseudo Code to Real Code: + - Overview: Lectures/Authentication/22/Overview.md + - Resource: Lectures/Authentication/22/resource.md + + - Implement JWT and Refactor The Project Structure: + - Overview: Lectures/Authentication/23/Overview.md + + - Implement User CRUD Operations: + - Overview: Lectures/Authentication/24/Overview.md + + - QNA on 5 Recorded Courses and Motivational ADDA: + - Overview: Lectures/Authentication/25/Overview.md + + - Implement Attendance System Main Functionalities: + - Overview: ./Lectures/Authentication/26/Overview.md + - Frontend: + - Frontend Core Concepts and Communication: + - Overview: ./Lectures/Frontend/27/Overview.md + # - Resource: Lectures/Frontend/27/resource.md + - Frontend Course Planning & Discussion: + - Overview: ./Lectures/Frontend/28/Overview.md + # - Resource: Lectures/Frontend/27/resource.md + - Understand React in A Different Way: + - Overview: ./Lectures/Frontend/29/Overview.md + # - Resource: Lectures/Frontend/27/resource.md + + + - Blog: + - blog/index.md + - 2022: + - blog/articles/we-need-freedom.md + - blog/articles/application-requirements-and-landscape.md + - blog/articles/understand-programming-languages.md + + + - "Hosting and Deployment": + - "Combinations": "hosting-and-deployment/combinations.md" + - "Netlify": "hosting-and-deployment/netlify.md" + - "GitHub Pages": "hosting-and-deployment/github-pages.md" + - "GitLab Pages": "hosting-and-deployment/gitlab-pages.md" + - "AWS Amplify Console": "hosting-and-deployment/aws-amplify-console.md" diff --git a/netlify.toml b/netlify.toml new file mode 100644 index 0000000..d1967db --- /dev/null +++ b/netlify.toml @@ -0,0 +1,6 @@ +[build] + command = "mkdocs build --config-file mkdocs.yml" + publish = "site" + +[build.environment] + RUBY_VERSION = "2.7.4" diff --git a/tasks.py b/tasks.py new file mode 100644 index 0000000..3e68729 --- /dev/null +++ b/tasks.py @@ -0,0 +1,35 @@ +from invoke import task +from time import sleep +import urllib.request, webbrowser, os +import concurrent.futures as confu + + +def open_browser(addr): + """ + Open browser + """ + url = "http://" + addr + while True: + sleep(0.1) + try: + with urllib.request.urlopen(url) as res: + if res: + break + except urllib.error.HTTPError as err: + pass + except urllib.error.URLError as err: + pass + webbrowser.open(url, new=2, autoraise=True) + + +@task(help={ + "config-file": "Provide a specific MkDocs config", + "dev-addr": "IP address and port to serve documentation locally (default: localhost:8000)" +}) +def serve(c, config_file="mkdocs.yml", dev_addr="localhost:8000"): + """ + Serve site and open browser + """ + with confu.ThreadPoolExecutor(max_workers=os.cpu_count()) as executor: + executor.submit(open_browser, dev_addr) + c.run(f"mkdocs serve --config-file {config_file} --dev-addr {dev_addr}")